
const STAGE_PROJECTS = [
  { id: 'presentation-agent', cover: '../../uploads/presentation-agent-cover.png', title: 'Presentation design agent', start: 'Sep 2026', status: 'done', at: 0.06, summary: 'Where the internship started.', about: ["Het bedrijf heeft een huisstijl met veel regels, maar alleen het marketingteam weet precies hoe je ze goed toepast. Wilden andere afdelingen een presentatie in de huisstijl, dan moesten ze daarvoor naar marketing.", "Voor dit project heb ik een design system in Claude gemaakt voor presentaties. Collega’s hoeven nu alleen nog de informatie aan te leveren die op de slide moet komen. De stijl wordt door het design system toegepast.", "Het design system was al ongeveer 75% af. Mijn taak was het afmaken: animaties en afbeeldingen toevoegen, het testen op fouten die het maakte en die oplossen."], updates: [{ title: "Startpunt", text: "Het design system was ongeveer 75% af.", note: "De kleuren in de presentatie zijn juist, maar worden niet consistent gebruikt.", problem: "De kaarten op de slide hebben niet allemaal dezelfde stijl, waardoor de presentatie slordig overkomt. Sommige kleuren passen ook niet bij elkaar, zoals de oranje lijn boven de turquoise kaart. Daarnaast komt de tone of voice van het bedrijf niet terug in de tekst.", images: ["../../uploads/Design%20system%20presentation/1791809630570-8baddb6a-c4ba-45c2-b018-5e60896d74c2_1.jpg", "../../uploads/Design%20system%20presentation/1791809630570-8baddb6a-c4ba-45c2-b018-5e60896d74c2_3.jpg", "../../uploads/Design%20system%20presentation/1791809630570-8baddb6a-c4ba-45c2-b018-5e60896d74c2_4.jpg"] }, { title: "Animaties", text: "Animaties toegevoegd aan de presentaties.", note: "Ik wilde animaties toevoegen aan de presentaties om ze interactiever te maken. Presentaties kunnen soms lang duren en met animaties zijn ze minder statisch. Eerst heb ik onderzoek gedaan naar hoe ik animaties in Claude Design kon krijgen. Daarbij vond ik Hyperframes, open-source software met een grote bibliotheek HTML-animaties die in een presentatie gebruikt kunnen worden.", problem: "Animaties waren in Claude Design eerst alleen mogelijk in videoformaat en niet in een presentatie.", fix: "Met Hyperframes kon het wel in presentatieformaat. Ik heb eerst zelf alleen de statische frames voor de animaties gemaakt, in de kleuren, lettertypes en spacing van het bedrijf. Hyperframes heeft daar vervolgens de animaties van gemaakt.", images: ["../../uploads/pasted-1791877218543-0.png", "../../uploads/Screenshot%202026-10-13%20094547.png"] }, { title: "Afbeeldingen", text: "Afbeeldingen toegevoegd aan het design system.", note: "Alle bedrijfsafbeeldingen staan op één plek opgeslagen. Die heb ik toegevoegd aan de design system-bibliotheek, in de daarvoor bestemde categorie.", problem: "Normaal gesproken stelt Claude eerst een aantal vragen voordat het een presentatie maakt, bijvoorbeeld hoeveel slides je wilt, of de presentatie voor intern of voor klanten is en of je animaties wilt. We hadden ook afgesproken dat het moest vragen of je afbeeldingen uit de bibliotheek wilt, je eigen afbeeldingen wilt uploaden of geen afbeeldingen wilt. Soms stelde Claude die vraag over afbeeldingen niet.", fix: "Ik heb dit opgelost door het in de code vast te leggen als harde regel. Claude moet de vraag over afbeeldingen stellen voordat het verdergaat.", images: ["../../uploads/1791878562600-cccac6f2-6ffa-4fce-8642-2bdaff4e2b94_1.jpg1024", "../../uploads/1791878562600-cccac6f2-6ffa-4fce-8642-2bdaff4e2b94_2.jpg1024", "../../uploads/1791878562600-cccac6f2-6ffa-4fce-8642-2bdaff4e2b94_3.jpg1024"] }, { title: "Testen en verbeteren", text: "Het design system getest op fouten die het maakte en deze opgelost.", note: "Ik heb meerdere presentaties gemaakt met het oude design system en gekeken waar het soms misging. Dat heb ik genoteerd, zodat ik het handmatig kon oplossen.", problem: "Een van de grootste problemen was inconsistentie op sommige slides. Bij bijvoorbeeld drie of vier kaarten waren er soms een paar wit met een gekleurde lijn erboven en één kaart helemaal zwart. Daarnaast zette het systeem soms kleuren bij elkaar die volgens de huisstijl niet samen mogen.", fix: "Ik heb de kaarten zo aangepast dat ze altijd dezelfde stijl hebben. Ook heb ik de kleurcombinaties gecorrigeerd, zodat alleen combinaties worden gebruikt die volgens de huisstijl zijn toegestaan.", images: ["../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_1.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_2.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_3.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_4.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_5.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_6.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_7.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_8.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_9.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_10.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_11.jpg", "../../uploads/KOR%20design%20system%20showcase/1791879047863-fec41a3e-9e1a-4d01-8867-468e7ed7a620_12.jpg"] }] },
  { id: 'video-agent', cover: '../../uploads/video-agent-cover-v3.png', title: 'Video editing agent', start: 'Oct 2026', status: 'now', at: 0.3, summary: 'Currently working on this one.', about: ["We zijn met dit project begonnen omdat we trainingsvideo’s moesten maken over hoe je Claude gebruikt en wat AI-fluency is.", "Bij het bewerken van de video’s liepen we tegen een probleem aan: de laptop die ik gebruikte kon Adobe Premiere Pro niet aan. Ik moest dus een andere manier zoeken om video’s te bewerken.", "Ik wist dat er een makkelijkere manier moest zijn, bijvoorbeeld met AI. Als dat werkt voor de trainingsvideo’s, dan moet het ook werken voor onze advertenties."], updates: [{ title: "Versie 1", group: "Trainingsvideo’s", note: "Omdat ik Hyperframes al had gebruikt in het presentatieproject, dacht ik dat het ook mogelijk moest zijn om de HTML-animaties toe te voegen aan de video’s die we hadden opgenomen. Ook moest de AI automatisch de fouten uit de opnames kunnen knippen en alle clips samenvoegen tot één video.", problem: "Dat werkte in versie 1 niet perfect. De sneden waren inconsistent: soms knipte de AI belangrijke dingen weg of midden in een zin. Daardoor voelde de video abrupt.", fix: "Daarom heb ik de sneden gemaakt en de clips samengevoegd in Clipchamp. Daarna heb ik Hyperframes gebruikt om de animaties aan de video toe te voegen.", images: ["../../uploads/pasted-1791882070590-0.png", "../../uploads/pasted-1791882257565-0.png", "../../uploads/pasted-1791882433703-0.png"] }, { title: "Versie 2", noProblem: true, note: "In de tweede versie heb ik de video-editing agent eerst een aantal video’s gegeven die ik zelf had geknipt en samengevoegd. Zo weet de agent hoe ik video’s wil laten knippen.", fix: "We hebben ervoor gezorgd dat de sneden een vloeiendere overgang hebben, zodat ze niet opvallen. Ook hebben we de animaties aangepast, omdat ze saai waren en niet genoeg de aandacht van de kijker trokken.", images: ["../../uploads/pasted-1791883824819-0.png", "../../uploads/pasted-1791883850947-0.png", "../../uploads/pasted-1791883914985-0.png"] }, { title: "Versie 3", group: "Advertenties · nog te testen", upcoming: true, noProblem: true, note: "Het systeem voor trainingsvideo’s is klaar. Voor advertenties weten we nog niet of hetzelfde systeem werkt of dat er een ander formaat nodig is. Dat moeten we nog testen." }] },
  { id: 'email-agent', title: 'Email design agent', start: 'Mid Nov 2026', status: 'next', at: 0.5, summary: 'Up next.', updates: [] },
  { id: 'social-agent', title: 'Social posts design agent', start: 'Later', status: 'next', at: 0.72, summary: '', updates: [] },
  { id: 'more', title: 'And more', start: 'Feb 2027', status: 'more', at: 0.93, summary: '', updates: [] }
];
const STAGE_MONTHS = ['Sep', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb'];
const STAGE_TODAY = 0.25;
const RM_PATH = 'M50 330 C 170 330, 210 120, 360 140 S 540 380, 660 330 S 830 90, 950 150';
const RM_W = 1000, RM_H = 460;
const RM_SIDE = { 'presentation-agent': 0, 'video-agent': -1, 'email-agent': 1, 'social-agent': -1, more: -1 };

function StageRoadmap({ onNavigate }) {
  const h = React.createElement;
  const wrapRef = React.useRef(null), pathRef = React.useRef(null);
  const [pts, setPts] = React.useState(null), [hover, setHover] = React.useState(null), [narrow, setNarrow] = React.useState(false);
  React.useLayoutEffect(() => {
    const p = pathRef.current; if (!p) return;
    const L = p.getTotalLength();
    const at = f => { const q = p.getPointAtLength(L * f); return { x: q.x, y: q.y }; };
    setPts({ months: STAGE_MONTHS.map((m, i) => ({ m, ...at(i / (STAGE_MONTHS.length - 1)) })), stops: STAGE_PROJECTS.map(s => ({ ...s, ...at(s.at) })), today: at(STAGE_TODAY) });
  }, [narrow]);
  React.useEffect(() => {
    const el = wrapRef.current; if (!el) return;
    const ro = new ResizeObserver(() => setNarrow(el.getBoundingClientRect().width < 0));
    ro.observe(el); return () => ro.disconnect();
  }, []);
  const shell = { position: 'relative', borderRadius: 'var(--radius-lg, 24px)', overflow: 'visible' };
  if (narrow) return h('div', { ref: wrapRef, style: { ...shell, display: 'grid', gap: 'var(--space-3)' } },
    STAGE_PROJECTS.map(s => h('button', { key: s.id, onClick: s.status === 'next' ? undefined : () => onNavigate('STAGE PROJECT', s.id), style: { display: 'flex', alignItems: 'center', gap: 'var(--space-4)', textAlign: 'left', border: s.status === 'next' ? '2px dashed var(--green-700)' : 'none', borderRadius: 'var(--radius-md)', background: s.status === 'next' ? 'transparent' : 'var(--green-700)', padding: 'var(--space-4)', cursor: s.status === 'next' ? 'default' : 'pointer' } },
      h('img', { src: '../../assets/star-' + (s.status === 'next' ? 'green' : 'white') + '.png', alt: '', style: { width: 44, height: 44, objectFit: 'contain' } }),
      h('span', null, h('span', { style: { display: 'block', fontFamily: 'var(--font-display)', fontSize: 20, color: s.status === 'next' ? 'var(--green-700)' : 'var(--white)' } }, s.title), h('span', { style: { fontFamily: 'var(--font-body)', fontSize: 'var(--text-small)', color: s.status === 'next' ? 'var(--text-muted)' : 'rgba(255,255,255,.85)' } }, s.start + (s.status === 'now' ? ' \u00b7 Now' : ''))))));
  const split = t => { const w = t.split(' '); if (w.length < 3) return [t]; const m = Math.ceil(w.length / 2); return [w.slice(0, m).join(' '), w.slice(m).join(' ')]; };
  const font = { fontFamily: 'var(--font-body)' };
  return h('div', { ref: wrapRef, style: shell },
    h('div', { style: { paddingBottom: 'var(--space-3)' } }, h('svg', { viewBox: '0 0 ' + RM_W + ' ' + RM_H, style: { display: 'block', width: '100%', height: 'auto' } },
      h('path', { ref: pathRef, d: RM_PATH, fill: 'none', stroke: 'var(--green-700)', strokeOpacity: .4, strokeWidth: 3, strokeDasharray: '2 12', strokeLinecap: 'round' }),
      h('path', { d: RM_PATH, fill: 'none', stroke: 'var(--green-700)', strokeWidth: 7, strokeLinecap: 'round', pathLength: 1, strokeDasharray: STAGE_TODAY + ' 1' }),
      pts && pts.months.map(m => h('g', { key: m.m },
        h('circle', { cx: m.x, cy: m.y, r: 6, fill: 'var(--surface-page)', stroke: 'var(--green-700)', strokeWidth: 2 }),
        h('text', { x: m.x, y: m.y + 28, textAnchor: 'middle', fill: 'var(--green-900)', style: { ...font, fontSize: 14, fontWeight: 700, letterSpacing: '.12em', textTransform: 'uppercase' } }, m.m))),
      pts && h('g', null, h('circle', { cx: pts.today.x, cy: pts.today.y, r: 9, fill: 'var(--green-700)' }), h('circle', { cx: pts.today.x, cy: pts.today.y, r: 9, fill: 'none', stroke: 'var(--green-700)', strokeWidth: 2 }, h('animate', { attributeName: 'r', from: 9, to: 26, dur: '1.8s', repeatCount: 'indefinite' }), h('animate', { attributeName: 'opacity', from: 0.8, to: 0, dur: '1.8s', repeatCount: 'indefinite' }))),
      pts && pts.stops.map(s => {
        const now = s.status === 'now', next = s.status !== 'now' && s.status !== 'done', more = s.status === 'more', r = now ? 40 : more ? 20 : 36, side = RM_SIDE[s.id], lines = split(s.title);
        const lx = side === 0 ? s.x + r + 16 : s.x;
        const ty = side === 0 ? s.y + 14 : side < 0 ? s.y - r - 18 - (lines.length - 1) * 26 - 22 : s.y + r + 28;
        const star = '../../assets/star-' + (next ? 'green' : 'white') + '.png';
        return h('g', { key: s.id, onClick: next ? undefined : () => onNavigate('STAGE PROJECT', s.id), onMouseEnter: () => setHover(s.id), onMouseLeave: () => setHover(null), style: { cursor: next ? 'default' : 'pointer' } },
          h('g', { style: { transformBox: 'fill-box', transformOrigin: 'center', transform: hover === s.id && !next ? 'scale(1.1)' : 'none', transition: 'transform .25s ease' } },
            h('circle', { cx: s.x, cy: s.y, r, fill: next ? 'var(--surface-page)' : 'var(--green-700)', stroke: next ? 'var(--green-700)' : 'none', strokeWidth: 2, strokeDasharray: next ? '6 6' : 'none' }),
            !more && h('foreignObject', { x: s.x - r * .55, y: s.y - r * .55, width: r * 1.1, height: r * 1.1 }, h('img', { src: star, alt: '', style: { width: '100%', height: '100%', objectFit: 'contain', display: 'block', opacity: next ? .6 : 1 } }))),
          now && h('g', null, h('rect', { x: s.x + r - 26, y: s.y - r - 6, width: 56, height: 26, rx: 13, fill: 'var(--green-900)', stroke: 'var(--surface-page)', strokeWidth: 2 }), h('text', { x: s.x + r + 2, y: s.y - r + 12, textAnchor: 'middle', fill: 'var(--white)', style: { ...font, fontSize: 12, fontWeight: 700, letterSpacing: '.1em' } }, 'NOW')),
          h('text', { textAnchor: side === 0 ? 'start' : 'middle', fill: 'var(--green-900)', opacity: next ? .75 : 1, style: { pointerEvents: 'none' } },
            lines.map((l, i) => h('tspan', { key: i, x: lx, y: ty + i * 26, style: { fontFamily: 'var(--font-display)', fontSize: 24 } }, l)),
            h('tspan', { x: lx, y: ty + lines.length * 26 + 2, fill: 'var(--text-muted)', style: { ...font, fontSize: 15 } }, s.start + (next ? '' : ' \u00b7 view progress \u2192'))));
      }))));
}
Object.assign(window, { StageRoadmap, STAGE_PROJECTS });
