// Main portfolio app — case study browser with vertical carousel.
// Layout: top nav (name + about), left detail card, right vertical carousel + dots.

const CASE_STUDIES = [
{
  id: 'mvno',
  number: '01',
  sector: 'Telecom',
  title: 'MVNO Mobile App',
  description:
  'See how a self-service app for a Polish mobile operator was shaped end to end — from research to Design System.',
  accent: '#B0EBBB',
  Phone: 'MVNOPhone'
},
{
  id: 'ecom',
  number: '02',
  sector: 'Cashback',
  title: 'Cashback Mobile App',
  description:
  'Discover how three separate web platforms became one coherent mobile product — from full redesign to building a new brand experience from the ground up.',
  accent: '#FCC00D',
  Phone: 'EcomPhone'
},
{
  id: 'delivery',
  number: '03',
  sector: 'Logistics',
  title: 'UXCam Research',
  description:
  'See how UXCam analysis helped solve a parcel delivery problem through a user-centred app redesign.',
  accent: '#FF5757',
  Phone: 'DeliveryPhone'
},
{
  id: 'fitness',
  number: '04',
  sector: 'Health',
  title: 'Fitness Management App',
  description:
  'See how Personas, Customer Journeys and a Service Blueprint mapped the touchpoints between users and the system across the entire fitness service.',
  accent: '#1E88E5',
  Phone: 'FitnessPhone'
}];


// Theme palettes
const THEMES = {
  teal: {
    bg: '#16323D',
    fg: '#F1E9DA',
    fgMuted: 'rgba(241,233,218,.65)',
    fgDim: 'rgba(241,233,218,.4)',
    accent: '#E6B17A',
    line: 'rgba(241,233,218,.15)',
    previewSurface: '#0f242c',
    previewFg: '#F1E9DA',
    previewAccent: '#E6B17A',
    name: 'Editorial Teal'
  },
  cream: {
    bg: '#E8DFCB',
    fg: '#2a1f1a',
    fgMuted: 'rgba(42,31,26,.7)',
    fgDim: 'rgba(42,31,26,.4)',
    accent: '#C4533F',
    line: 'rgba(42,31,26,.12)',
    previewSurface: '#fff',
    previewFg: '#2a1f1a',
    previewAccent: '#C4533F',
    name: 'Warm Cream'
  }
};

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "teal",
  "animation": "slide",
  "peekSize": 0.6
} /*EDITMODE-END*/;

const Arrow = ({ dir = 'right', size = 22, stroke = 1.7 }) => {
  const r = { right: 0, left: 180, up: -90, down: 90 }[dir] ?? 0;
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
    style={{ transform: `rotate(${r}deg)`, display: 'block' }}>
      <path d="M5 12h13M12 6l6 6-6 6" stroke="currentColor" strokeWidth={stroke}
      strokeLinecap="round" strokeLinejoin="round" />
    </svg>);

};

// ── Case detail (left side)
const CaseDetail = ({ data, theme, animation, version, onOpen, onOpenCase }) => {
  const Phone = window[data.Phone];
  const caseAccent = data.accent || theme.accent;
  return (
    <div style={{
      display: 'flex', flexDirection: 'column', height: '100%',
      paddingRight: 32,
      position: 'relative',
      minHeight: 660
    }}>
      {/* Meta */}
      <div className="case-meta" style={{ position: 'relative' }}>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 14,
          fontFamily: 'DM Sans, sans-serif', fontSize: 12,
          letterSpacing: '.14em', textTransform: 'uppercase',
          color: theme.fgMuted,
          transition: 'color .5s ease'
        }}>
          <span style={{ fontFamily: 'Libre Bodoni, serif', fontSize: 14, color: caseAccent,
            letterSpacing: 0, textTransform: 'none', fontWeight: 500,
            transition: 'color .5s ease' }}>
            {data.number}
          </span>
          <span style={{ width: 28, height: 1, background: theme.line }} />
          <span>{data.sector}</span>
        </div>

        {/* Title — fixed-height slot so layout doesn't jump between cases */}
        <div style={{ minHeight: 184, marginTop: 22, display: 'flex', alignItems: 'flex-start' }}>
          <h1 key={`title-${version}`} style={{
            fontFamily: 'Libre Bodoni, serif',
            fontWeight: 500,
            fontSize: 'clamp(42px, 4.4vw, 60px)',
            lineHeight: 1.02,
            margin: 0,
            color: caseAccent,
            letterSpacing: '-.02em',
            textWrap: 'balance',
            transition: 'color .5s ease',
            animation: 'caseTextIn .55s cubic-bezier(.2,.7,.2,1) forwards'
          }}>{data.title}</h1>
        </div>

        {/* Description — fixed min-height slot */}
        <div style={{ minHeight: 132 }}>
          <p key={`desc-${version}`} style={{
            fontFamily: 'DM Sans, sans-serif',
            fontSize: 16, lineHeight: 1.55,
            marginTop: 4, marginBottom: 0,
            color: theme.fg, opacity: .82,
            maxWidth: 460, textWrap: 'pretty',
            animation: 'caseTextIn .55s .08s cubic-bezier(.2,.7,.2,1) both'
          }}>{data.description}</p>
        </div>

        {/* CTA */}
        <button onClick={() => onOpenCase ? onOpenCase(data.id) : onOpen()} style={{
          appearance: 'none', border: 'none', background: 'none', padding: 0,
          marginTop: 24, cursor: 'pointer',
          display: 'inline-flex', alignItems: 'center', gap: 14,
          color: theme.fg, fontFamily: 'DM Sans, sans-serif',
          fontSize: 13, fontWeight: 600, letterSpacing: '.18em', textTransform: 'uppercase'
        }}
        onMouseEnter={(e) => e.currentTarget.querySelector('.cta-line').style.width = '64px'}
        onMouseLeave={(e) => e.currentTarget.querySelector('.cta-line').style.width = '32px'}>
          Open case study
          <span className="cta-line" style={{
            position: 'relative', width: 32, height: 1.4,
            background: theme.fg, transition: 'width .35s cubic-bezier(.2,.7,.2,1)',
            display: 'inline-block'
          }}>
            <span style={{
              position: 'absolute', right: -1, top: -3.5,
              borderTop: `1.4px solid ${theme.fg}`, borderRight: `1.4px solid ${theme.fg}`,
              width: 7, height: 7, transform: 'rotate(45deg)'
            }} />
          </span>
        </button>
      </div>

      {/* Animated preview anchored to the bottom of the left column — scaled down */}
      <div style={{
        position: 'absolute', left: 0, bottom: 0,
        transform: 'scale(0.5)', transformOrigin: 'bottom left',
        pointerEvents: 'auto'
      }}>
        <AnimatedPreview
          caseId={data.id}
          animationType={animation}
          accent={caseAccent}
          surface={theme.previewSurface}
          fg={theme.previewFg}
          version={version} />
        
      </div>
    </div>);

};

// ── Vertical carousel (right side)
const CASE_HEIGHT = 460; // height of central phone
const GAP = 48; // gap between center and peek

const Carousel = ({ index, setIndex, theme, peekSize, isDragging, setIsDragging, dragOffsetRef, dotsHovered, setDotsHovered, onOpenCase }) => {
  const total = CASE_STUDIES.length;
  const [offset, setOffset] = React.useState(0); // px offset during drag

  // Drag handlers
  React.useEffect(() => {
    if (!isDragging) {
      setOffset(0);
      return undefined;
    }
    const onMove = (e) => {
      const dy = (e.clientY ?? e.touches?.[0]?.clientY) - dragOffsetRef.current.startY;
      setOffset(dy);
    };
    const onUp = (e) => {
      const dy = (e.clientY ?? e.changedTouches?.[0]?.clientY) - dragOffsetRef.current.startY;
      const threshold = 80;
      if (dy < -threshold) setIndex((i) => Math.min(total - 1, i + 1));else
      if (dy > threshold) setIndex((i) => Math.max(0, i - 1));
      setOffset(0);
      setIsDragging(false);
    };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
    return () => {
      window.removeEventListener('pointermove', onMove);
      window.removeEventListener('pointerup', onUp);
    };
  }, [isDragging, total]);

  const onPointerDown = (e) => {
    dragOffsetRef.current.startY = e.clientY;
    setIsDragging(true);
  };

  return (
    <>
      {/* Phone stack — absolutely centered inside the main, shifts right by default */}
      <div
        onPointerDown={onPointerDown}
        style={{
          position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          userSelect: 'none', cursor: isDragging ? 'grabbing' : 'grab',
          touchAction: 'none', pointerEvents: 'none', zIndex: 6
        }}>
        <div style={{
          position: 'relative', height: CASE_HEIGHT, width: 220, pointerEvents: 'auto',
          transform: dotsHovered ? 'translateX(0)' : 'translateX(min(20vw, 240px))',
          transition: 'transform .65s cubic-bezier(.5,0,.2,1)'
        }}>
          {CASE_STUDIES.map((c, i) => {
            const rel = i - index; // -1 above, 0 center, +1 below
            const visible = Math.abs(rel) <= 1;
            if (!visible) return null;
            const Phone = window[c.Phone];
            const baseY = rel * (CASE_HEIGHT * (1 + peekSize) / 2 - 70);
            const scale = rel === 0 ? 1 : peekSize;
            const opacity = rel === 0 ? 1 : 0.6;
            return (
              <div key={c.id}
              onClick={(e) => {if (rel === 0 && onOpenCase) {e.stopPropagation();onOpenCase(c.id);}}}
              style={{
                position: 'absolute', top: '50%', left: '50%',
                transform: `translate(-50%, calc(-50% + ${baseY + offset * 0.4}px)) scale(${scale})`,
                transformOrigin: 'center',
                opacity,
                transition: isDragging ? 'none' : 'transform .8s cubic-bezier(.7,0,.2,1), opacity .5s ease',
                pointerEvents: rel === 0 ? 'auto' : 'none',
                cursor: rel === 0 && onOpenCase ? 'pointer' : 'inherit',
                zIndex: rel === 0 ? 2 : 1,
                filter: rel === 0 ? 'none' : 'saturate(0.7) brightness(0.95)'
              }}>
                <Phone width={220} />
              </div>);

          })}
        </div>
      </div>

      {/* Dots — pinned to right edge */}
      <div style={{
        position: 'absolute', right: 56, top: '50%', transform: 'translateY(-50%)',
        display: 'flex', flexDirection: 'column', gap: 18, alignItems: 'flex-end',
        zIndex: 10
      }}
      data-hover={dotsHovered ? '1' : '0'}
      onMouseEnter={() => setDotsHovered(true)}
      onMouseLeave={() => setDotsHovered(false)}>
        {CASE_STUDIES.map((c, i) => {
          const on = i === index;
          const dotAccent = c.accent || theme.accent;
          return (
            <button key={c.id}
            className="dot-btn"
            data-on={on ? '1' : '0'}
            onMouseEnter={() => {if (i !== index) setIndex(i);}}
            onPointerDown={(e) => {e.stopPropagation();}}
            onClick={() => setIndex(i)}
            style={{
              appearance: 'none', border: 'none', padding: '6px 0', margin: 0,
              background: 'none', cursor: 'pointer',
              display: 'flex', alignItems: 'center', gap: 14,
              color: theme.fg,
              '--dot-accent': dotAccent
            }}>
              <span className="dot-label" style={{
                fontFamily: 'DM Sans, sans-serif', fontSize: 12,
                letterSpacing: '.14em', textTransform: 'uppercase',
                fontWeight: 600,
                whiteSpace: 'nowrap',
                transition: 'color .3s ease, opacity .35s ease',
                fontVariantNumeric: 'tabular-nums',
                display: 'inline-flex', alignItems: 'baseline', gap: 10
              }}>
                <span className="dot-num" style={{
                  opacity: .55, fontSize: 10, letterSpacing: '.12em',
                  fontWeight: 500
                }}>{c.number}</span>
                <span className="dot-title">{c.title}</span>
              </span>
              <span className="dot-circle" style={{
                width: on ? 11 : 7, height: on ? 11 : 7, borderRadius: '50%',
                background: on ? dotAccent : theme.fgMuted,
                transition: 'all .35s cubic-bezier(.4,.7,.3,1)',
                boxShadow: on ? `0 0 0 4px ${dotAccent}22` : 'none',
                flexShrink: 0
              }} />
            </button>);

        })}
      </div>
      <style>{`
        .dot-btn .dot-label       { color: ${theme.fgMuted}; opacity: 0; pointer-events: none;
                                    transition: color .25s ease, opacity .35s ease; }
        .dot-btn .dot-title       { opacity: 0; transition: opacity .35s ease; }

        /* Hover on dots column — every label appears */
        [data-hover="1"] .dot-btn .dot-label,
        [data-hover="1"] .dot-btn .dot-title { opacity: 1; }
        [data-hover="1"] .dot-btn[data-on="1"] .dot-label { color: ${theme.fg}; }

        /* Hovered button — highlighted in its own accent */
        [data-hover="1"] .dot-btn:hover .dot-label,
        [data-hover="1"] .dot-btn:hover .dot-num   { color: var(--dot-accent); }
      `}</style>
    </>);

};

// ── About panel — slides in from right
const ABOUT_ORANGE = '#E8743B';
const AboutPanel = ({ open, onClose, theme }) => {
  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 50,
      pointerEvents: open ? 'auto' : 'none'
    }}>
      {/* Backdrop */}
      <div onClick={onClose} style={{
        position: 'absolute', inset: 0,
        background: theme.bg, opacity: open ? 0.4 : 0,
        transition: 'opacity .55s ease'
      }} />
      {/* Sheet */}
      <div style={{
        position: 'absolute', top: 0, right: 0, bottom: 0,
        width: '100%',
        background: theme.bg,
        color: theme.fg,
        transform: open ? 'translateX(0)' : 'translateX(100%)',
        transition: 'transform .65s cubic-bezier(.7,0,.2,1)',
        boxShadow: '-20px 0 60px rgba(0,0,0,.25)',
        padding: 'clamp(48px, 7vw, 96px) clamp(40px, 8vw, 140px)',
        overflowY: 'auto'
      }}>
        <div style={{ maxWidth: 1000, margin: '0 auto' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 64 }}>
          <div style={{
            fontFamily: 'DM Sans, sans-serif', fontSize: 11.5,
            letterSpacing: '.2em', textTransform: 'uppercase', color: theme.fgMuted
          }}>About</div>
          <button onClick={onClose} style={{
            appearance: 'none', border: 'none', background: 'none', cursor: 'pointer',
            color: theme.fg, padding: 8, display: 'flex', alignItems: 'center', gap: 12,
            fontFamily: 'DM Sans, sans-serif', fontSize: 13, letterSpacing: '.16em',
            textTransform: 'uppercase', fontWeight: 600
          }}>
            <span>Close</span>
            <span style={{ position: 'relative', width: 22, height: 22 }}>
              <span style={{ position: 'absolute', inset: '50% 0 auto 0', height: 1.5, background: 'currentColor', transform: 'rotate(45deg)' }} />
              <span style={{ position: 'absolute', inset: '50% 0 auto 0', height: 1.5, background: 'currentColor', transform: 'rotate(-45deg)' }} />
            </span>
          </button>
        </div>

        <h2 style={{
          fontFamily: 'Libre Bodoni, serif', fontWeight: 500,
          fontSize: 'clamp(48px, 5.4vw, 80px)', lineHeight: 1, margin: 0,
          letterSpacing: '-.02em', textWrap: 'balance'
        }}>
          Joanna Kiljan
        </h2>
        <div style={{
          fontFamily: 'DM Sans, sans-serif', fontWeight: 600,
          fontSize: 'clamp(16px, 1.4vw, 19px)',
          letterSpacing: '.16em', textTransform: 'uppercase',
          color: ABOUT_ORANGE, marginTop: 18
        }}>
          UX/UI Designer
        </div>

        <p style={{
          fontFamily: 'DM Sans, sans-serif',
          fontSize: 17, lineHeight: 1.62, color: theme.fg, opacity: .9,
          maxWidth: 620, marginTop: 36, textWrap: 'pretty'
        }}>
          I’m a UX/UI Designer who likes turning messy problems into clear, useful
          digital experiences. I work across research, UX, UI, design systems, and
          implementation support, so I care not only about how a product looks, but
          also how it works, scales, and helps people get things done.
        </p>
        <p style={{
          fontFamily: 'DM Sans, sans-serif',
          fontSize: 17, lineHeight: 1.62, color: theme.fg, opacity: .9,
          maxWidth: 620, marginTop: 20, textWrap: 'pretty'
        }}>
          I enjoy working close to users, teams, and business goals — finding the small
          moments where a better decision can make the whole experience feel simpler,
          smarter, and more human.
        </p>

        {/* Sections */}
        <div style={{ marginTop: 72, display: 'grid',
                       gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 48 }}>
          {[
          ['Focus', ['Product Design', 'Design System', 'UX Research', 'Prototyping']],
          ['Tools', ['Figma', 'UXCam / Hotjar', 'Claude Design', 'SQL Server']],
          ['Experience', ['UX/UI Design', 'Business Analysis', 'Testing', 'Mentor']],
          ['Contact', ['joannakiljan7@gmail.com', '506-144-585']]].
          map(([title, items]) =>
          <div key={title}>
              <div style={{
              fontFamily: 'DM Sans, sans-serif', fontSize: 11,
              letterSpacing: '.2em', textTransform: 'uppercase',
              color: ABOUT_ORANGE, marginBottom: 16,
              paddingBottom: 12, borderBottom: `1px solid ${theme.line}`, fontWeight: 600
            }}>{title}</div>
              {items.map((it) =>
            <div key={it} style={{
              fontFamily: 'DM Sans, sans-serif', fontSize: 14.5,
              color: theme.fg, opacity: .88, padding: '7px 0'
            }}>{it}</div>
            )}
            </div>
          )}
        </div>
        </div>
      </div>
    </div>);

};

// ── Top bar
const TopBar = ({ theme, aboutOpen, setAboutOpen }) =>
<header style={{
  position: 'absolute', top: 0, left: 0, right: 0,
  padding: '34px 56px',
  display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start',
  zIndex: 20, pointerEvents: 'none'
}}>
    <div style={{ pointerEvents: 'auto' }}>
      <div style={{
      fontFamily: 'DM Sans, sans-serif',
      fontWeight: 500, fontSize: 16,
      color: theme.fg, letterSpacing: '.01em', lineHeight: 1
    }}>
        Joanna Kiljan
      </div>
    </div>

    <button onClick={() => setAboutOpen(true)}
  style={{
    pointerEvents: 'auto',
    appearance: 'none', border: 'none', background: 'none', cursor: 'pointer',
    color: theme.fg, padding: 0,
    display: 'inline-flex', alignItems: 'center', gap: 14,
    fontFamily: 'DM Sans, sans-serif', fontSize: 13,
    letterSpacing: '.18em', textTransform: 'uppercase', fontWeight: 600
  }}>
      <span>About</span>
      <span style={{
      width: 38, height: 1.4, background: theme.fg, position: 'relative'
    }}>
        <span style={{
        position: 'absolute', right: -1, top: -3.5,
        borderTop: `1.4px solid ${theme.fg}`, borderRight: `1.4px solid ${theme.fg}`,
        width: 7, height: 7, transform: 'rotate(45deg)'
      }} />
      </span>
    </button>
  </header>;


// ── App
function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const theme = THEMES[t.theme] || THEMES.teal;
  const [index, setIndex] = React.useState(0);
  const [aboutOpen, setAboutOpen] = React.useState(false);
  const [isDragging, setIsDragging] = React.useState(false);
  const [dotsHovered, setDotsHovered] = React.useState(false);
  const [openCaseId, setOpenCaseId] = React.useState(null);
  const dragOffsetRef = React.useRef({ startY: 0 });

  const current = CASE_STUDIES[index];

  const onOpenCase = React.useCallback((caseId) => {
    if (caseId === 'mvno') setOpenCaseId('mvno');else
    if (caseId === 'ecom') setOpenCaseId('ecom');else
    if (caseId === 'delivery') setOpenCaseId('delivery');else
    if (caseId === 'fitness') setOpenCaseId('fitness');else
    alert(`Case study "${CASE_STUDIES.find((c) => c.id === caseId)?.title}" — coming soon.`);
  }, []);

  // Track wheel scroll → navigate carousel
  const lastWheelRef = React.useRef(0);
  React.useEffect(() => {
    const onWheel = (e) => {
      if (aboutOpen) return;
      const now = Date.now();
      if (now - lastWheelRef.current < 600) return;
      if (Math.abs(e.deltaY) < 14) return;
      lastWheelRef.current = now;
      if (e.deltaY > 0) setIndex((i) => Math.min(CASE_STUDIES.length - 1, i + 1));else
      setIndex((i) => Math.max(0, i - 1));
    };
    window.addEventListener('wheel', onWheel, { passive: true });
    return () => window.removeEventListener('wheel', onWheel);
  }, [aboutOpen]);

  // Keyboard
  React.useEffect(() => {
    const onKey = (e) => {
      if (e.key === 'Escape' && aboutOpen) setAboutOpen(false);
      if (aboutOpen) return;
      if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
        e.preventDefault();
        setIndex((i) => Math.max(0, i - 1));
      }
      if (e.key === 'ArrowDown' || e.key === 'ArrowRight' || e.key === ' ') {
        e.preventDefault();
        setIndex((i) => Math.min(CASE_STUDIES.length - 1, i + 1));
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [aboutOpen]);

  return (
    <div style={{
      minHeight: '100vh',
      background: theme.bg,
      color: theme.fg,
      transition: 'background .6s ease, color .6s ease',
      overflow: 'hidden',
      position: 'relative'
    }}>
      <style>{`
        @keyframes caseTextIn {
          0%   { transform: translateY(8px); }
          100% { transform: translateY(0); }
        }
        body, html, #root { margin: 0; padding: 0; }
        * { box-sizing: border-box; }
        ::selection { background: ${theme.accent}; color: ${theme.bg}; }
      `}</style>

      <TopBar theme={theme} aboutOpen={aboutOpen} setAboutOpen={setAboutOpen} />

      {/* Body */}
      <main style={{
        position: 'relative',
        minHeight: '100vh',
        padding: '120px 56px 64px'
      }}>
        {/* Left text column — fixed width, hidden on dots hover */}
        <section style={{
          position: 'relative', zIndex: 2,
          width: 'min(36%, 420px)',
          minHeight: 560,
          pointerEvents: dotsHovered ? 'none' : 'auto',
          opacity: dotsHovered ? 0 : 1,
          transform: dotsHovered ? 'translateX(-12px)' : 'translateX(0)',
          transition: 'opacity .45s ease, transform .55s cubic-bezier(.5,0,.2,1)'
        }}>
          <CaseDetail data={current} theme={theme} animation={t.animation} version={index}
          onOpen={() => alert(`Open case study: ${current.title}`)}
          onOpenCase={onOpenCase} />
        </section>

        {/* Black overlay — covers the page when dots are hovered */}
        <div style={{
          position: 'fixed', inset: 0,
          background: '#000',
          opacity: dotsHovered ? 1 : 0,
          pointerEvents: 'none',
          transition: 'opacity .5s ease',
          zIndex: 4
        }} />

        {/* Carousel — phones centered in viewport, dots pinned right */}
        <Carousel
          index={index}
          setIndex={setIndex}
          theme={theme}
          peekSize={t.peekSize}
          isDragging={isDragging}
          setIsDragging={setIsDragging}
          dragOffsetRef={dragOffsetRef}
          dotsHovered={dotsHovered}
          setDotsHovered={setDotsHovered}
          onOpenCase={onOpenCase} />

        {/* Bottom marker */}
        <div style={{
          position: 'absolute', bottom: 24, left: 56,
          fontFamily: 'DM Sans, sans-serif', fontSize: 10.5,
          letterSpacing: '.2em', textTransform: 'uppercase',
          color: theme.fgDim,
          opacity: dotsHovered ? 0 : 1,
          transition: 'opacity .35s ease'
        }}>
          Scroll · Arrows · Drag — to navigate
        </div>
        <div style={{
          position: 'absolute', bottom: 24, right: 56,
          fontFamily: 'DM Sans, sans-serif', fontSize: 10.5,
          letterSpacing: '.2em', textTransform: 'uppercase',
          color: theme.fgDim, fontVariantNumeric: 'tabular-nums',
          opacity: dotsHovered ? 0 : 1,
          transition: 'opacity .35s ease'
        }}>
          <span style={{ color: theme.fg }}>{String(index + 1).padStart(2, '0')}</span>
          <span style={{ opacity: .5 }}> / {String(CASE_STUDIES.length).padStart(2, '0')}</span>
        </div>
      </main>

      <AboutPanel open={aboutOpen} onClose={() => setAboutOpen(false)} theme={theme} />

      <MVNOCaseStudy open={openCaseId === 'mvno'} onClose={() => setOpenCaseId(null)} />
      <CashbackCaseStudy open={openCaseId === 'ecom'} onClose={() => setOpenCaseId(null)} />
      <DeliveryCaseStudy open={openCaseId === 'delivery'} onClose={() => setOpenCaseId(null)} />
      <FitnessCaseStudy open={openCaseId === 'fitness'} onClose={() => setOpenCaseId(null)} />

      <TweaksPanel title="Portfolio tweaks">
        <TweakSection label="Aesthetic">
          <TweakRadio label="Theme" value={t.theme}
          options={[{ value: 'teal', label: 'Teal' }, { value: 'cream', label: 'Cream' }]}
          onChange={(v) => setTweak('theme', v)} />
        </TweakSection>
        <TweakSection label="Live preview animation">
          <TweakRadio label="Type" value={t.animation}
          options={[
          { value: 'screens', label: 'Fade' },
          { value: 'slide', label: 'Slide' },
          { value: 'tap', label: 'Tap' }]
          }
          onChange={(v) => setTweak('animation', v)} />
        </TweakSection>
        <TweakSection label="Carousel">
          <TweakSlider label="Neighbour peek" value={t.peekSize}
          min={0.4} max={0.85} step={0.05}
          onChange={(v) => setTweak('peekSize', v)} />
        </TweakSection>
      </TweaksPanel>
    </div>);

}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);