Last active
March 27, 2026 18:16
-
-
Save maxparm/aa9a16f053c7011ff998b931358a8804 to your computer and use it in GitHub Desktop.
Hack Week: Draft Topics — The Missing Layer Between Meetings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Draft Topics: The Platform Primitive Topicflow Needs</title> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap'); | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'Inter', -apple-system, sans-serif; | |
| background: #0f0f13; | |
| color: #e4e4e7; | |
| overflow: hidden; | |
| height: 100vh; | |
| } | |
| .deck { | |
| display: flex; | |
| transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1); | |
| height: 100vh; | |
| } | |
| .slide { | |
| min-width: 100vw; | |
| height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| padding: 6vh 10vw; | |
| position: relative; | |
| } | |
| .progress { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| height: 3px; | |
| background: linear-gradient(90deg, #7c3aed, #a78bfa); | |
| transition: width 0.5s ease; | |
| z-index: 100; | |
| } | |
| .counter { | |
| position: fixed; | |
| bottom: 2vh; | |
| right: 3vw; | |
| font-size: 0.8rem; | |
| color: #52525b; | |
| z-index: 100; | |
| font-variant-numeric: tabular-nums; | |
| } | |
| .nav-hint { | |
| position: fixed; | |
| bottom: 2vh; | |
| left: 3vw; | |
| font-size: 0.75rem; | |
| color: #3f3f46; | |
| z-index: 100; | |
| } | |
| /* Typography */ | |
| h1 { | |
| font-size: clamp(2.5rem, 5vw, 4rem); | |
| font-weight: 800; | |
| line-height: 1.1; | |
| letter-spacing: -0.03em; | |
| margin-bottom: 1.5rem; | |
| } | |
| h2 { | |
| font-size: clamp(1.8rem, 3.5vw, 2.8rem); | |
| font-weight: 700; | |
| line-height: 1.15; | |
| letter-spacing: -0.02em; | |
| margin-bottom: 1.5rem; | |
| } | |
| h3 { | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| color: #a78bfa; | |
| text-transform: uppercase; | |
| letter-spacing: 0.1em; | |
| margin-bottom: 0.75rem; | |
| } | |
| p, li { | |
| font-size: clamp(1rem, 1.5vw, 1.25rem); | |
| line-height: 1.7; | |
| color: #a1a1aa; | |
| max-width: 48rem; | |
| } | |
| .subtitle { | |
| font-size: clamp(1.1rem, 2vw, 1.4rem); | |
| color: #71717a; | |
| font-weight: 400; | |
| max-width: 40rem; | |
| } | |
| .accent { color: #a78bfa; } | |
| .accent-green { color: #4ade80; } | |
| .accent-amber { color: #fbbf24; } | |
| .accent-rose { color: #fb7185; } | |
| .dim { color: #52525b; } | |
| .bright { color: #e4e4e7; } | |
| /* Lists */ | |
| ul { list-style: none; padding: 0; } | |
| ul li { | |
| padding: 0.5rem 0; | |
| padding-left: 1.5rem; | |
| position: relative; | |
| } | |
| ul li::before { | |
| content: ''; | |
| position: absolute; | |
| left: 0; | |
| top: 1rem; | |
| width: 6px; | |
| height: 6px; | |
| border-radius: 50%; | |
| background: #7c3aed; | |
| } | |
| /* Flow diagram */ | |
| .flow { | |
| display: flex; | |
| align-items: center; | |
| gap: 0; | |
| margin: 2.5rem 0; | |
| flex-wrap: wrap; | |
| } | |
| .flow-node { | |
| padding: 1rem 1.5rem; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 0.95rem; | |
| white-space: nowrap; | |
| } | |
| .flow-arrow { | |
| color: #3f3f46; | |
| font-size: 1.5rem; | |
| padding: 0 0.5rem; | |
| } | |
| .flow-node.purple { background: #7c3aed22; border: 1px solid #7c3aed55; color: #c4b5fd; } | |
| .flow-node.green { background: #22c55e18; border: 1px solid #22c55e44; color: #86efac; } | |
| .flow-node.amber { background: #f59e0b18; border: 1px solid #f59e0b44; color: #fcd34d; } | |
| .flow-node.zinc { background: #27272a; border: 1px solid #3f3f46; color: #a1a1aa; } | |
| .flow-node.rose { background: #f4336618; border: 1px solid #f4336644; color: #fca5a5; } | |
| /* Callout */ | |
| .callout { | |
| border-left: 3px solid #7c3aed; | |
| padding: 1rem 1.5rem; | |
| margin: 2rem 0; | |
| background: #7c3aed08; | |
| border-radius: 0 8px 8px 0; | |
| max-width: 40rem; | |
| } | |
| .callout p { | |
| color: #c4b5fd; | |
| font-size: 1.15rem; | |
| font-style: italic; | |
| font-weight: 400; | |
| } | |
| /* Stacked items */ | |
| .stack { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| margin: 1.5rem 0; | |
| max-width: 36rem; | |
| } | |
| .stack-item { | |
| display: flex; | |
| gap: 1rem; | |
| align-items: flex-start; | |
| } | |
| .stack-icon { | |
| font-size: 1.5rem; | |
| flex-shrink: 0; | |
| width: 2rem; | |
| text-align: center; | |
| } | |
| .stack-item h4 { | |
| font-size: 1rem; | |
| font-weight: 600; | |
| color: #e4e4e7; | |
| margin-bottom: 0.2rem; | |
| } | |
| .stack-item p { | |
| font-size: 0.9rem; | |
| line-height: 1.5; | |
| color: #71717a; | |
| } | |
| /* Title slide */ | |
| .slide.title-slide { justify-content: center; } | |
| .slide.title-slide h1 { font-size: clamp(3rem, 6vw, 5rem); } | |
| .tag { | |
| display: inline-block; | |
| padding: 0.3rem 0.75rem; | |
| border-radius: 999px; | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| margin-bottom: 1.5rem; | |
| } | |
| .tag.purple { background: #7c3aed22; color: #a78bfa; border: 1px solid #7c3aed44; } | |
| /* Object definition card */ | |
| .object-def { | |
| background: #18181b; | |
| border: 1px solid #27272a; | |
| border-radius: 12px; | |
| padding: 1.5rem 2rem; | |
| margin: 2rem 0; | |
| max-width: 36rem; | |
| font-family: 'Inter', monospace; | |
| } | |
| .object-def .obj-name { | |
| color: #a78bfa; | |
| font-weight: 700; | |
| font-size: 1.1rem; | |
| margin-bottom: 1rem; | |
| } | |
| .object-def .obj-field { | |
| display: flex; | |
| gap: 0.75rem; | |
| padding: 0.3rem 0; | |
| font-size: 0.9rem; | |
| } | |
| .object-def .obj-key { | |
| color: #71717a; | |
| min-width: 9rem; | |
| } | |
| .object-def .obj-val { | |
| color: #d4d4d8; | |
| } | |
| /* Sources funnel diagram */ | |
| .sources-diagram { | |
| display: flex; | |
| align-items: center; | |
| gap: 1.5rem; | |
| margin: 2rem 0; | |
| } | |
| .sources-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .source-item { | |
| padding: 0.6rem 1.2rem; | |
| border-radius: 10px; | |
| font-size: 0.85rem; | |
| font-weight: 600; | |
| white-space: nowrap; | |
| } | |
| .source-item.s1 { background: #f4336618; border: 1px solid #f4336644; color: #fca5a5; } | |
| .source-item.s2 { background: #7c3aed18; border: 1px solid #7c3aed44; color: #c4b5fd; } | |
| .source-item.s3 { background: #22c55e12; border: 1px solid #22c55e33; color: #86efac; } | |
| .source-item.s4 { background: #27272a; border: 1px solid #3f3f46; color: #a1a1aa; } | |
| .funnel-arrow { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| color: #3f3f46; | |
| font-size: 2rem; | |
| gap: 0; | |
| line-height: 1; | |
| } | |
| .draft-box { | |
| padding: 1.25rem 2rem; | |
| border-radius: 12px; | |
| background: #f59e0b12; | |
| border: 2px solid #f59e0b55; | |
| text-align: center; | |
| } | |
| .draft-box h4 { | |
| color: #fcd34d; | |
| font-size: 1.1rem; | |
| font-weight: 700; | |
| margin-bottom: 0.3rem; | |
| } | |
| .draft-box p { | |
| color: #71717a; | |
| font-size: 0.8rem; | |
| line-height: 1.4; | |
| } | |
| .manager-box { | |
| padding: 1rem 1.5rem; | |
| border-radius: 12px; | |
| background: #27272a; | |
| border: 1px solid #3f3f46; | |
| text-align: center; | |
| } | |
| .manager-box h4 { | |
| color: #e4e4e7; | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| margin-bottom: 0.2rem; | |
| } | |
| .manager-box p { | |
| color: #52525b; | |
| font-size: 0.75rem; | |
| } | |
| .meeting-box { | |
| padding: 1.25rem 2rem; | |
| border-radius: 12px; | |
| background: #22c55e12; | |
| border: 2px solid #22c55e44; | |
| text-align: center; | |
| } | |
| .meeting-box h4 { | |
| color: #86efac; | |
| font-size: 1.1rem; | |
| font-weight: 700; | |
| margin-bottom: 0.3rem; | |
| } | |
| .meeting-box p { | |
| color: #71717a; | |
| font-size: 0.8rem; | |
| line-height: 1.4; | |
| } | |
| /* Kanban mockup */ | |
| .kanban-mock { | |
| display: flex; | |
| gap: 1rem; | |
| margin: 2rem 0; | |
| overflow-x: auto; | |
| padding-bottom: 0.5rem; | |
| } | |
| .kanban-col { | |
| min-width: 180px; | |
| border-radius: 10px; | |
| padding: 0.75rem; | |
| flex-shrink: 0; | |
| } | |
| .kanban-col.ai { background: #7c3aed12; border: 1px solid #7c3aed33; } | |
| .kanban-col.draft { background: #18181b; border: 1px solid #27272a; } | |
| .kanban-col.meeting { background: #18181b; border: 1px solid #27272a; } | |
| .kanban-col-title { | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| margin-bottom: 0.5rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.4rem; | |
| } | |
| .kanban-col.ai .kanban-col-title { color: #a78bfa; } | |
| .kanban-col.draft .kanban-col-title { color: #71717a; } | |
| .kanban-col.meeting .kanban-col-title { color: #71717a; } | |
| .kanban-card { | |
| background: #27272a; | |
| border: 1px solid #3f3f46; | |
| border-radius: 8px; | |
| padding: 0.6rem 0.75rem; | |
| font-size: 0.78rem; | |
| color: #d4d4d8; | |
| margin-bottom: 0.4rem; | |
| line-height: 1.4; | |
| } | |
| .kanban-col.ai .kanban-card { | |
| background: #7c3aed15; | |
| border-color: #7c3aed33; | |
| } | |
| /* Loop / proved it */ | |
| .loop-diagram { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| margin: 2rem 0; | |
| flex-wrap: wrap; | |
| } | |
| .loop-step { | |
| padding: 0.85rem 1.25rem; | |
| border-radius: 10px; | |
| font-weight: 600; | |
| font-size: 0.85rem; | |
| text-align: center; | |
| min-width: 120px; | |
| } | |
| .loop-step.step-ai { background: #7c3aed18; border: 1px solid #7c3aed44; color: #c4b5fd; } | |
| .loop-step.step-draft { background: #f59e0b12; border: 1px solid #f59e0b44; color: #fcd34d; } | |
| .loop-step.step-meeting { background: #22c55e12; border: 1px solid #22c55e44; color: #86efac; } | |
| .loop-step.step-signal { background: #27272a; border: 1px solid #3f3f46; color: #a1a1aa; } | |
| .loop-arrow { | |
| color: #3f3f46; | |
| font-size: 1.3rem; | |
| } | |
| .loop-return { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| margin-top: 0.75rem; | |
| color: #52525b; | |
| font-size: 0.85rem; | |
| } | |
| .loop-return .return-line { | |
| flex: 1; | |
| height: 1px; | |
| background: #27272a; | |
| } | |
| /* Stat callout */ | |
| .stat { | |
| display: inline-block; | |
| font-size: clamp(3rem, 5vw, 4.5rem); | |
| font-weight: 800; | |
| color: #a78bfa; | |
| letter-spacing: -0.03em; | |
| line-height: 1; | |
| margin-bottom: 0.5rem; | |
| } | |
| .stat-label { | |
| font-size: clamp(1.1rem, 2vw, 1.5rem); | |
| color: #71717a; | |
| font-weight: 400; | |
| max-width: 32rem; | |
| } | |
| /* Screenshot placeholder */ | |
| .screenshot-placeholder { | |
| background: #18181b; | |
| border: 2px dashed #27272a; | |
| border-radius: 12px; | |
| padding: 3rem 2rem; | |
| text-align: center; | |
| margin: 1.5rem 0; | |
| max-width: 56rem; | |
| } | |
| .screenshot-placeholder p { | |
| color: #3f3f46; | |
| font-size: 0.9rem; | |
| text-align: center; | |
| max-width: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="progress" id="progress"></div> | |
| <div class="counter" id="counter"></div> | |
| <div class="nav-hint">Arrow keys or click to navigate</div> | |
| <div class="deck" id="deck"> | |
| <!-- SLIDE 1: Title --> | |
| <section class="slide title-slide"> | |
| <span class="tag purple">Hack Week 2026</span> | |
| <h1>Draft Topics</h1> | |
| <p class="subtitle">Topicflow needs a meeting-free object. Here's why, and here's what I built.</p> | |
| </section> | |
| <!-- SLIDE 2: The Problem (one slide, sharp) --> | |
| <section class="slide"> | |
| <h3>The Problem</h3> | |
| <div class="stat">0</div> | |
| <p class="stat-label">external systems can create an object in Topicflow today without knowing which meeting to put it in.</p> | |
| <div class="callout" style="margin-top: 2.5rem;"> | |
| <p>Imagine if Linear required every issue to be scheduled into a Sprint the moment it was created. No backlog. No triage. Just: pick a Sprint or it doesn't exist.</p> | |
| </div> | |
| <p style="margin-top: 1.5rem;">That's us. A Slack bot, an AI agent, a webhook — they all hit the same wall: <em>you can't create a topic without a meeting.</em></p> | |
| </section> | |
| <!-- SLIDE 3: The Primitive --> | |
| <section class="slide"> | |
| <h3>The Primitive</h3> | |
| <h2>A topic that exists<br><span class="accent">without a meeting</span></h2> | |
| <div class="object-def"> | |
| <div class="obj-name">DraftTopic</div> | |
| <div class="obj-field"><span class="obj-key">title</span><span class="obj-val">"Follow up on oncall rotation"</span></div> | |
| <div class="obj-field"><span class="obj-key">relationship</span><span class="obj-val">Manager ↔ Report</span></div> | |
| <div class="obj-field"><span class="obj-key">notes</span><span class="obj-val">Rich text (facilitator / subject)</span></div> | |
| <div class="obj-field"><span class="obj-key">meeting</span><span class="obj-val accent-rose" style="color: #fb7185;">null</span></div> | |
| </div> | |
| <p>Scoped to a relationship, not a meeting. The manager decides <em>if</em> and <em>when</em> it reaches the agenda. The creator just needs to know who it's about.</p> | |
| </section> | |
| <!-- SLIDE 4: What This Unlocks (the money diagram) --> | |
| <section class="slide"> | |
| <h3>What This Unlocks</h3> | |
| <h2>Many sources, <span class="accent">one inbox</span>,<br>manager decides</h2> | |
| <div class="sources-diagram"> | |
| <div class="sources-list"> | |
| <div class="source-item s1">Slack bot</div> | |
| <div class="source-item s2">AI coaching agent</div> | |
| <div class="source-item s3">Integration / webhook</div> | |
| <div class="source-item s4">Manager (manual)</div> | |
| </div> | |
| <div class="funnel-arrow">→</div> | |
| <div class="draft-box"> | |
| <h4>Draft Topics</h4> | |
| <p>Refine, add notes,<br>decide importance</p> | |
| </div> | |
| <div class="funnel-arrow">→</div> | |
| <div class="manager-box"> | |
| <h4>Manager curates</h4> | |
| <p>promote / dismiss / wait</p> | |
| </div> | |
| <div class="funnel-arrow">→</div> | |
| <div class="meeting-box"> | |
| <h4>Meeting Agenda</h4> | |
| <p>Only what's ready,<br>when it's ready</p> | |
| </div> | |
| </div> | |
| <p>Creation is decoupled from scheduling. Any tool writes to the inbox. The manager owns the agenda.</p> | |
| </section> | |
| <!-- SLIDE 5: Demo — what I built --> | |
| <section class="slide"> | |
| <h3>What I Built This Week</h3> | |
| <h2>A kanban board for<br><span class="accent">every manager ↔ report pair</span></h2> | |
| <div class="kanban-mock"> | |
| <div class="kanban-col ai"> | |
| <div class="kanban-col-title">✨ AI Insights</div> | |
| <div class="kanban-card">Talk about career growth — no discussion in 3 months</div> | |
| <div class="kanban-card">Recognize recent shipping velocity</div> | |
| </div> | |
| <div class="kanban-col draft"> | |
| <div class="kanban-col-title">📝 Draft Topics</div> | |
| <div class="kanban-card">Discuss Q2 project ownership</div> | |
| <div class="kanban-card">Follow up on conference talk idea</div> | |
| </div> | |
| <div class="kanban-col meeting"> | |
| <div class="kanban-col-title">📅 Next 1-on-1 — Apr 1</div> | |
| <div class="kanban-card">Q1 retro & wins</div> | |
| </div> | |
| <div class="kanban-col meeting"> | |
| <div class="kanban-col-title">📅 Last 1-on-1 — Mar 25</div> | |
| <div class="kanban-card">Team reorg update</div> | |
| <div class="kanban-card">On-call rotation concerns</div> | |
| </div> | |
| </div> | |
| <p><span class="bright">AI Insights</span> column loads coaching suggestions on demand. <span class="bright">"Add to next meeting"</span> promotes a draft with one click. Below the board: collapsible <span class="bright">Discussed</span>, <span class="bright">Undiscussed</span>, and <span class="bright">Dismissed</span> lists.</p> | |
| <p style="margin-top: 0.5rem; color: #52525b; font-size: 0.85rem;">Backend: new DraftTopic fields on BaseTopic + kanban_meetings resolver. Frontend: full working board at /kanban.</p> | |
| </section> | |
| <!-- SLIDE 6: The loop I proved --> | |
| <section class="slide"> | |
| <h3>The Loop I Proved</h3> | |
| <h2>AI insight → draft → agenda → <span class="accent">signal</span></h2> | |
| <div class="loop-diagram"> | |
| <div class="loop-step step-ai">AI surfaces<br>coaching insight</div> | |
| <div class="loop-arrow">→</div> | |
| <div class="loop-step step-draft">Manager accepts<br>as draft topic</div> | |
| <div class="loop-arrow">→</div> | |
| <div class="loop-step step-meeting">Promoted to<br>meeting agenda</div> | |
| <div class="loop-arrow">→</div> | |
| <div class="loop-step step-signal">Discussed?<br>Dismissed?</div> | |
| </div> | |
| <div class="loop-return"> | |
| <span>←</span> | |
| <span class="return-line"></span> | |
| <span style="color: #71717a;">Outcome feeds back into AI — learns what this manager cares about</span> | |
| </div> | |
| <ul style="margin-top: 2rem;"> | |
| <li>This loop <span class="bright">works end-to-end</span> in the hack week build</li> | |
| <li>Every accept, dismiss, and promotion is a <span class="bright">signal</span> the current system doesn't capture</li> | |
| <li>Replace "AI" with "Slack bot" or "webhook" and the loop still works — the primitive is the same</li> | |
| </ul> | |
| </section> | |
| <!-- SLIDE 7: Strategic — why this matters --> | |
| <section class="slide"> | |
| <h3>Why This Matters</h3> | |
| <h2>Every feature gets better when it can <span class="accent">read and write drafts</span></h2> | |
| <div class="stack"> | |
| <div class="stack-item"> | |
| <div class="stack-icon">🔌</div> | |
| <div> | |
| <h4>Integrations become possible</h4> | |
| <p>Slack, GitHub, Jira, HRIS systems — all can write draft topics without understanding our calendar or meeting model.</p> | |
| </div> | |
| </div> | |
| <div class="stack-item"> | |
| <div class="stack-icon">🤖</div> | |
| <div> | |
| <h4>AI agents get a landing pad</h4> | |
| <p>Agents detect events (missed goals, PR patterns, recognition moments) and push drafts. The manager curates, not the bot.</p> | |
| </div> | |
| </div> | |
| <div class="stack-item"> | |
| <div class="stack-icon">📋</div> | |
| <div> | |
| <h4>Reviews write themselves</h4> | |
| <p>Draft history + notes = a real-time log of manager observations. No more "try to remember 6 months of context."</p> | |
| </div> | |
| </div> | |
| <div class="stack-item"> | |
| <div class="stack-icon">📈</div> | |
| <div> | |
| <h4>Meeting prep goes from shallow to deep</h4> | |
| <p>AI sees accumulated context between meetings, not just what was discussed last time.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- SLIDE 8: What's Next --> | |
| <section class="slide"> | |
| <h3>What's Next</h3> | |
| <h2>From hack week to <span class="accent">roadmap</span></h2> | |
| <div class="stack"> | |
| <div class="stack-item"> | |
| <div class="stack-icon">🔌</div> | |
| <div> | |
| <h4>Slack bot: create drafts from chat</h4> | |
| <p><span style="color: #d4d4d8; font-family: monospace; font-size: 0.85rem;">/topicflow draft @sarah Follow up on oncall rotation</span></p> | |
| </div> | |
| </div> | |
| <div class="stack-item"> | |
| <div class="stack-icon">🔗</div> | |
| <div> | |
| <h4>Public API / webhook endpoint</h4> | |
| <p>Any third-party system can push a draft topic. HR tools, project trackers, performance platforms.</p> | |
| </div> | |
| </div> | |
| <div class="stack-item"> | |
| <div class="stack-icon">🤖</div> | |
| <div> | |
| <h4>AI agent auto-drafting</h4> | |
| <p>Proactive agents that detect signals and create drafts without manager intervention.</p> | |
| </div> | |
| </div> | |
| <div class="stack-item"> | |
| <div class="stack-icon">📊</div> | |
| <div> | |
| <h4>Feed draft history into reviews</h4> | |
| <p>The accumulated log of topics, notes, and outcomes becomes source material for performance reviews.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- SLIDE 9: Close --> | |
| <section class="slide title-slide"> | |
| <div class="stat" style="margin-bottom: 1rem;">0 → ∞</div> | |
| <h2>Today, zero external systems can create<br>a topic in Topicflow.</h2> | |
| <p class="subtitle" style="margin-top: 1rem;">Draft topics change that number to <em>all of them</em>.<br>The manager stays in control. The platform opens up.</p> | |
| <p style="margin-top: 3rem; color: #52525b; font-size: 0.9rem;">hackweek · march 2026 · maxime</p> | |
| </section> | |
| </div> | |
| <script> | |
| let current = 0; | |
| const deck = document.getElementById('deck'); | |
| const progress = document.getElementById('progress'); | |
| const counter = document.getElementById('counter'); | |
| const slides = document.querySelectorAll('.slide'); | |
| const total = slides.length; | |
| function goTo(n) { | |
| current = Math.max(0, Math.min(n, total - 1)); | |
| deck.style.transform = `translateX(-${current * 100}vw)`; | |
| progress.style.width = `${((current + 1) / total) * 100}%`; | |
| counter.textContent = `${current + 1} / ${total}`; | |
| } | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'ArrowRight' || e.key === ' ') { e.preventDefault(); goTo(current + 1); } | |
| if (e.key === 'ArrowLeft') { e.preventDefault(); goTo(current - 1); } | |
| }); | |
| document.addEventListener('click', (e) => { | |
| if (e.clientX > window.innerWidth / 2) goTo(current + 1); | |
| else goTo(current - 1); | |
| }); | |
| let touchStartX = 0; | |
| document.addEventListener('touchstart', (e) => { touchStartX = e.touches[0].clientX; }); | |
| document.addEventListener('touchend', (e) => { | |
| const diff = touchStartX - e.changedTouches[0].clientX; | |
| if (Math.abs(diff) > 50) { diff > 0 ? goTo(current + 1) : goTo(current - 1); } | |
| }); | |
| goTo(0); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment