feat: add matrix-themed interactive red/blue pill choice slide with digital rain effect

This commit is contained in:
Luke Hewitt 2026-06-29 16:18:02 +10:00
parent cc3cceabf3
commit 51272f5312
No known key found for this signature in database
GPG key ID: 1899A98EB9FC78E9
3 changed files with 140 additions and 17 deletions

View file

@ -394,26 +394,29 @@
</aside>
</section>
<!-- Slide 10: The Choice -->
<!-- Slide 10: The Choice (Blue Pill vs Red Pill) -->
<section id="slide-choice" class="slide-content" data-transition="slide-in fade-out">
<canvas id="matrix-canvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; opacity: 0; transition: opacity 0.5s ease; border-radius: 12px;"></canvas>
<h2 class="slide-header">The Choice for 2026 Grads</h2>
<div class="grid-2col margin-top-md">
<div class="card glassmorphic choice-card locked-opacity" id="choice-admin">
<h3 class="card-title text-grey">License Administrator</h3>
<p class="choice-text">Spend the next thirty years managing vendor lock-in, filling out procurement forms, and apologizing to users for interface problems you aren't allowed to fix.</p>
<div class="choice-footer text-red">The Status Quo</div>
<div class="card glassmorphic choice-card shadow-blue animate-pulse-border" id="choice-admin">
<div class="card-badge bg-blue">BLUE PILL</div>
<h3 class="card-title text-blue">License Administrator</h3>
<p class="choice-text">Take the blue pill. You exit this room, forget this presentation ever happened, and wake up tomorrow believing whatever you want to believe. You spend the next forty years managing cloud lock-in, configuring dropdowns in proprietary vendor systems, and paying digital rent to a foreign landlord.</p>
<div class="choice-footer text-blue">The Status Quo</div>
</div>
<div class="card glassmorphic choice-card highlight-cyan-glow animate-pulse-border" id="choice-builder">
<h3 class="card-title text-cyan">Sovereign Builder</h3>
<p class="choice-text">Champion open standards, use AI to create incredible local systems, and actually own the infrastructure that runs this state.</p>
<div class="choice-footer text-cyan">The Future Architecture</div>
<div class="card glassmorphic choice-card locked-opacity" id="choice-builder">
<div class="card-badge bg-red">RED PILL</div>
<h3 class="card-title text-red">Sovereign Builder</h3>
<p class="choice-text">Take the red pill. You stay in Wonderland, and we show you how deep the open-source rabbit hole goes. You use AI to build custom local systems, champion open standards, retain your digital borders, and actually own the infrastructure that runs this state.</p>
<div class="choice-footer text-red">Digital Sovereignty</div>
</div>
</div>
<aside class="notes">
You are the 2026 digital graduates. In five years, you are going to be the lead enterprise architects, the senior BAs, and the design directors for the Queensland government. You have a choice regarding what your career looks like. You can be a professional license administrator, managing vendor lock-in. Or, you can be builders. You can champion open standards, use AI to create incredible local systems, and actually own the infrastructure that runs this state. When something breaks, you won't be submitting a ticket to a vendor in another timezone; you'll be fixing it because you own it.
You are the 2026 digital graduates. In five years, you are going to be the lead enterprise architects, the senior BAs, and the design directors for the Queensland government. You have a choice regarding what your career looks like. You can take the blue pill and be a license administrator—spending the next forty years managing vendor lock-in, configuring dropdowns in proprietary software, and apologizing to users for things you aren't allowed to fix. Or, you can take the red pill. You can stay in Wonderland and see how deep the open-source rabbit hole goes. You can be builders. You can champion open standards, use AI to create incredible local systems, and actually own the infrastructure that runs this state. Waking up to digital sovereignty means when something breaks, you own the fix.
</aside>
</section>

View file

@ -46,7 +46,7 @@ deck.initialize().then(() => {
setupChoiceCardsInteractivity();
});
// Setup click interactivity for the Choice cards on Slide 8
// Setup click interactivity for the Choice cards on Slide 10 (Blue Pill vs Red Pill)
function setupChoiceCardsInteractivity() {
document.addEventListener('click', (e) => {
const adminCard = document.getElementById('choice-admin');
@ -56,21 +56,122 @@ function setupChoiceCardsInteractivity() {
const clickedCard = e.target.closest('.choice-card');
if (clickedCard) {
if (clickedCard.id === 'choice-admin') {
// Highlight Admin card, dim Builder card
// Highlight Blue Pill (Admin), dim Red Pill (Builder)
adminCard.classList.remove('locked-opacity');
adminCard.classList.add('shadow-red', 'animate-pulse-border');
adminCard.classList.add('shadow-blue', 'animate-pulse-border');
builderCard.classList.add('locked-opacity');
builderCard.classList.remove('highlight-cyan-glow', 'animate-pulse-border');
builderCard.classList.remove('shadow-red', 'animate-pulse-border');
// Stop Matrix digital rain
handleMatrixRain(false);
} else if (clickedCard.id === 'choice-builder') {
// Highlight Builder card, dim Admin card
// Highlight Red Pill (Builder), dim Blue Pill (Admin)
builderCard.classList.remove('locked-opacity');
builderCard.classList.add('highlight-cyan-glow', 'animate-pulse-border');
builderCard.classList.add('shadow-red', 'animate-pulse-border');
adminCard.classList.add('locked-opacity');
adminCard.classList.remove('shadow-red', 'animate-pulse-border');
adminCard.classList.remove('shadow-blue', 'animate-pulse-border');
// Start Matrix digital rain falling from top
handleMatrixRain(true);
}
}
}
});
// Performance optimization: Stop Matrix rain when navigating away from the Choice slide
deck.on('slidechanged', (event) => {
if (event.currentSlide && event.currentSlide.id !== 'slide-choice') {
handleMatrixRain(false);
// Reset back to Blue Pill default when leaving the slide so it re-triggers fresh
const adminCard = document.getElementById('choice-admin');
const builderCard = document.getElementById('choice-builder');
if (adminCard && builderCard) {
adminCard.classList.remove('locked-opacity');
adminCard.classList.add('shadow-blue', 'animate-pulse-border');
builderCard.classList.add('locked-opacity');
builderCard.classList.remove('shadow-red', 'animate-pulse-border');
}
}
});
}
// Matrix Digital Rain Logic
let matrixInterval = null;
let resizeHandler = null;
function handleMatrixRain(start) {
const canvas = document.getElementById('matrix-canvas');
if (!canvas) return;
// Clean up existing running state
if (matrixInterval) {
clearInterval(matrixInterval);
matrixInterval = null;
}
if (resizeHandler) {
window.removeEventListener('resize', resizeHandler);
resizeHandler = null;
}
if (!start) {
canvas.style.opacity = 0;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
return;
}
// Start rain
canvas.style.opacity = 0.45; // Subtle background visibility
const ctx = canvas.getContext('2d');
resizeHandler = () => {
canvas.width = canvas.parentElement.offsetWidth;
canvas.height = canvas.parentElement.offsetHeight;
};
resizeHandler();
window.addEventListener('resize', resizeHandler);
const fontSize = 16;
const columns = Math.floor(canvas.width / fontSize);
const drops = Array(columns).fill(0).map(() => Math.floor(Math.random() * -20)); // staggered start points
// Classic Matrix digital rain characters
const matrixChars = "ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const charsArray = matrixChars.split("");
function draw() {
ctx.fillStyle = 'rgba(5, 7, 12, 0.08)'; // trails background matching the slide color
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#00ff41'; // Standard Matrix green
ctx.font = `bold ${fontSize}px monospace`;
for (let i = 0; i < drops.length; i++) {
// Draw random character
const char = charsArray[Math.floor(Math.random() * charsArray.length)];
const x = i * fontSize;
const y = drops[i] * fontSize;
// Make the head of the drop white for the classic matrix glow effect
if (drops[i] > 0) {
ctx.fillStyle = '#adffbc';
ctx.fillText(char, x, y);
ctx.fillStyle = '#00ff41';
} else {
ctx.fillText(char, x, y);
}
// Reset drop to top once it goes past canvas bottom (with randomness to stagger)
if (y > canvas.height && Math.random() > 0.975) {
drops[i] = 0;
}
drops[i]++;
}
}
matrixInterval = setInterval(draw, 35);
}

View file

@ -22,6 +22,10 @@
--cyan-glow: rgba(6, 182, 212, 0.15);
--cyan-border: rgba(6, 182, 212, 0.4);
--blue-bright: #3b82f6;
--blue-glow: rgba(59, 130, 246, 0.15);
--blue-border: rgba(59, 130, 246, 0.4);
--purple-bright: #a855f7;
--purple-glow: rgba(168, 85, 247, 0.15);
@ -249,6 +253,16 @@
border: 1px solid var(--cyan-border);
}
.bg-blue {
background: var(--blue-glow);
color: var(--blue-bright);
border: 1px solid var(--blue-border);
}
.text-blue {
color: var(--blue-bright) !important;
}
.card-title {
font-size: 1.1em !important;
font-weight: 600;
@ -273,6 +287,11 @@
box-shadow: 0 10px 30px rgba(6, 182, 212, 0.05);
}
.shadow-blue {
border-color: rgba(59, 130, 246, 0.2);
box-shadow: 0 10px 30px rgba(59, 130, 246, 0.05);
}
.highlight-cyan-glow {
border-color: var(--cyan-border);
box-shadow: 0 0 25px rgba(6, 182, 212, 0.1);