15 Commits
v1.0 ... main

Author SHA1 Message Date
avi
1850666468 Save point: remove BTC $100K+/Gold $3K+/dollar down 70% punch cards from Scene 4e 2026-05-18 11:04:34 -05:00
avi
a56d534964 Save point: add dedicated desktop view CSS with explicit data-view='desktop' 2026-05-18 10:53:08 -05:00
avi
f57caea2fe Add APK binary for direct download 2026-05-18 10:27:40 -05:00
avi
162ab71fce Add README with build/run instructions 2026-05-18 10:24:16 -05:00
avi
a7981552bc Save point: add apk.apk binary for direct download 2026-05-17 21:11:24 -05:00
avi
9e86ff94e9 Save point: use touchend-based double-tap detection for reliable skip on all scenes 2026-05-17 21:03:07 -05:00
avi
fc19f9a00a Save point: double-click/tap skips scene via synthetic Escape event 2026-05-17 20:17:15 -05:00
avi
62ccf26c0e Save point: stack money scene buttons vertically on mobile; halve rain speed on Android 2026-05-17 20:14:56 -05:00
avi
7eafd34e84 Save point: disable double-tap escape on mobile; add user-scalable=no 2026-05-17 20:09:37 -05:00
avi
4650ff0eaf Save point: make scene4 timeline buttons scroll with content instead of fixed 2026-05-17 20:07:03 -05:00
avi
9b88861d0f Save point: make scene3 choice buttons scroll with content instead of fixed 2026-05-17 20:02:56 -05:00
avi
a3ee05acca Save point: fix scene3 button overlap, add padding-bottom 2026-05-17 19:54:19 -05:00
avi
cef24e170c Save point: fix word breaking, button overlap, auto-select mobile in APK 2026-05-17 19:45:42 -05:00
avi
565c61600f Save point: fix text overlapping and button layout for vertical mobile view 2026-05-17 18:23:01 -05:00
avi
e8aef45cf3 Save point: optimize mobile view for vertical display, bump all text sizes and interactive canvas heights 2026-05-17 18:15:58 -05:00
5 changed files with 7011 additions and 66 deletions

45
README.md Normal file
View File

@@ -0,0 +1,45 @@
# Signal Elsewhere
An interactive educational experience about money, technology, food, and health — built as a Capacitor Android app.
## Development
The app is a single-page HTML/JS application. No build step is needed for the web version.
```bash
# Serve locally for development
python3 -m http.server 8080
# or
npx serve .
```
Open `http://localhost:8080` in your browser.
## Android Build
```bash
# Install dependencies
npm install
# Copy web assets to Android
npx cap copy
# Sync Capacitor config
npx cap sync android
# Open in Android Studio
npx cap open android
```
From Android Studio, build and run on a device or emulator.
## Structure
- `index.html` — main application source
- `www/` — production web directory (copied to Android)
- `android/` — Android project (Capacitor)
- `capacitor.config.json` — Capacitor configuration
## License
ISC

BIN
apk.apk Normal file

Binary file not shown.

View File

@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Signal Elsewhere</title> <title>Signal Elsewhere</title>
<style> <style>
* { * {
@@ -975,55 +975,94 @@
text-align: center; text-align: center;
} }
/* Mobile view overrides (tuned for large 2.5' x 4' display) */ /* Mobile view overrides (tuned for large 2.5' x 4' vertical display) */
html[data-view="mobile"] .scene { font-size: 1.6rem; padding: 1.5rem; } html[data-view="mobile"] .scene { font-size: 1.8rem; padding: 2rem; }
html[data-view="mobile"] .scene4text { max-width: 100%; } html[data-view="mobile"] .scene4text { max-width: 90%; overflow-wrap: break-word; }
html[data-view="mobile"] .s4visual, html[data-view="mobile"] .scene5text { max-width: 100%; } html[data-view="mobile"] .s4visual, html[data-view="mobile"] .scene5text { max-width: 90%; overflow-wrap: break-word; }
html[data-view="mobile"] .s5visual { max-width: 100%; } html[data-view="mobile"] .s5visual { max-width: 90%; }
html[data-view="mobile"] .s4tl { max-width: 100%; margin-bottom: 0.8rem; padding: 0.5rem 0; } html[data-view="mobile"] .s4tl { max-width: 90%; margin-bottom: 1rem; padding: 0.5rem 0; }
html[data-view="mobile"] .comp-table { font-size: 1.1rem; max-width: 100%; } html[data-view="mobile"] .comp-table { font-size: 1.2rem; max-width: 90%; }
html[data-view="mobile"] .comp-table th, html[data-view="mobile"] .comp-table td { padding: 0.6rem 0.8rem; } html[data-view="mobile"] .comp-table th, html[data-view="mobile"] .comp-table td { padding: 0.5rem 0.6rem; }
html[data-view="mobile"] .tl-year { font-size: 0.95rem; } html[data-view="mobile"] .tl-year { font-size: 1rem; }
html[data-view="mobile"] .tl-year::after { width: 8px; height: 8px; bottom: -0.4rem; } html[data-view="mobile"] .tl-year::after { width: 10px; height: 10px; bottom: -0.4rem; }
html[data-view="mobile"] .btn-row { gap: 1rem; bottom: 12%; } html[data-view="mobile"] .typewriter-line { white-space: normal; }
html[data-view="mobile"] .btnNext { padding: 1rem 2rem; font-size: 1.3rem; } html[data-view="mobile"] #textContainer { font-size: 1.8rem; overflow-wrap: break-word; width: 90vw; }
html[data-view="mobile"] .btn-row { position: static; flex-direction: column; align-items: center; gap: 1rem; margin-top: 2rem; transform: none; width: 100%; }
html[data-view="mobile"] .btnNext { padding: 1rem 2rem; font-size: 1.4rem; }
html[data-view="mobile"] #scene10e .btnNext { padding: 1rem 2rem; } html[data-view="mobile"] #scene10e .btnNext { padding: 1rem 2rem; }
html[data-view="mobile"] #scene3Title { font-size: 2.8rem; top: 1.5rem; left: 1.5rem; } html[data-view="mobile"] #scene3Title { font-size: 3rem; top: 1.5rem; left: 1.5rem; }
html[data-view="mobile"] #scene3 { padding: 4rem 1.5rem; } html[data-view="mobile"] #scene3 { padding: 4rem 1.5rem 12rem; }
html[data-view="mobile"] #scene3Text { margin-top: 2rem; } html[data-view="mobile"] #scene3Text { margin-top: 2rem; overflow-wrap: break-word; }
html[data-view="mobile"] #scene3ChoiceRow { flex-direction: column; bottom: 20%; gap: 1rem; } html[data-view="mobile"] #scene3ChoiceRow { flex-direction: column; position: static; gap: 1rem; width: 100%; align-items: center; margin-top: 2rem; transform: none; }
html[data-view="mobile"] .scene3ChoiceBtn { padding: 1.2rem 2rem; font-size: 1.4rem; } html[data-view="mobile"] .scene3ChoiceBtn { padding: 1.2rem 2rem; font-size: 1.6rem; }
html[data-view="mobile"] .punch-row { flex-direction: column; gap: 0.8rem; } html[data-view="mobile"] .punch-row { flex-direction: column; gap: 0.8rem; }
html[data-view="mobile"] .punch-card { padding: 1rem; font-size: 1.2rem; } html[data-view="mobile"] .punch-card { padding: 1rem; font-size: 1.3rem; }
html[data-view="mobile"] .punch-card .punch-icon { font-size: 2rem; } html[data-view="mobile"] .punch-card .punch-icon { font-size: 2.2rem; }
html[data-view="mobile"] .scene4sub { padding: 1rem 1rem 8rem; } html[data-view="mobile"] .scene4sub { padding: 1rem 1.5rem 16rem; overflow-wrap: break-word; }
html[data-view="mobile"] #scene4a { padding-top: 0; } html[data-view="mobile"] #scene4a { padding-top: 0; justify-content: flex-start; }
html[data-view="mobile"] .hub-title { font-size: 2.2rem; } html[data-view="mobile"] .hub-title { font-size: 2.4rem; }
html[data-view="mobile"] .hub-subtitle { font-size: 1.3rem; } html[data-view="mobile"] .hub-subtitle { font-size: 1.4rem; }
html[data-view="mobile"] .hub-btn { padding: 1rem 2rem; font-size: 1.3rem; } html[data-view="mobile"] .hub-btn { padding: 1rem 2rem; font-size: 1.4rem; }
html[data-view="mobile"] .hub-row { flex-direction: column; gap: 1rem; padding: 1rem 1.5rem; } html[data-view="mobile"] .hub-row { flex-direction: column; gap: 1rem; padding: 1rem 1.5rem; }
html[data-view="mobile"] #sceneRefs h2 { font-size: 1.6rem; } html[data-view="mobile"] #sceneRefs h2 { font-size: 1.8rem; }
html[data-view="mobile"] #sceneRefs li { font-size: 1.1rem; } html[data-view="mobile"] #sceneRefs li { font-size: 1.2rem; }
html[data-view="mobile"] #sceneRefs .refs-content { width: 95%; padding: 1.2rem; max-height: 90vh; } html[data-view="mobile"] #sceneRefs .refs-content { width: 95%; padding: 1.2rem; max-height: 90vh; }
html[data-view="mobile"] .callout-box { font-size: 1.2rem; padding: 1rem 1.2rem; } html[data-view="mobile"] .callout-box { font-size: 1.3rem; padding: 1rem 1.2rem; }
html[data-view="mobile"] #foodOverlay .food-box { padding: 2rem; } html[data-view="mobile"] #foodOverlay .food-box { padding: 2rem; }
html[data-view="mobile"] #foodOverlay .food-title { font-size: 1.6rem; } html[data-view="mobile"] #foodOverlay .food-title { font-size: 1.8rem; }
html[data-view="mobile"] #foodOverlay .food-btn { font-size: 1.3rem; padding: 1rem 1.5rem; } html[data-view="mobile"] #foodOverlay .food-btn { font-size: 1.4rem; padding: 1rem 1.5rem; }
html[data-view="mobile"] #sceneFoodLinks .food-btn { font-size: 1.3rem; padding: 1rem 1.5rem; } html[data-view="mobile"] #sceneFoodLinks .food-btn { font-size: 1.4rem; padding: 1rem 1.5rem; }
html[data-view="mobile"] #sceneFoodLinks .food-links-title { font-size: 1.8rem; } html[data-view="mobile"] #sceneFoodLinks .food-links-title { font-size: 2rem; }
html[data-view="mobile"] canvas { max-height: 350px; } html[data-view="mobile"] #meshCanvas,
html[data-view="mobile"] #scene2Line1 { font-size: 1.6rem; padding: 1.5rem; } html[data-view="mobile"] #deskCanvas,
html[data-view="mobile"] #scene2Message2 { font-size: 1.6rem; } html[data-view="mobile"] #phoneCanvas,
html[data-view="mobile"] #textContainer { font-size: 1.6rem; } html[data-view="mobile"] #exfilCanvas,
html[data-view="mobile"] .coin-grid { max-width: 20rem; gap: 5px; } html[data-view="mobile"] #exfilDeskCanvas { max-height: 500px; }
html[data-view="mobile"] .scene5text { font-size: 1.4rem; } html[data-view="mobile"] #scene2Line1 { font-size: 1.8rem; padding: 1.5rem; overflow-wrap: break-word; }
html[data-view="mobile"] .hash-bar { height: 3.5rem; } html[data-view="mobile"] #scene2Message2 { font-size: 1.8rem; }
html[data-view="mobile"] #s7BtnRow { bottom: 12%; } html[data-view="mobile"] .coin-grid { max-width: 24rem; gap: 5px; }
html[data-view="mobile"] #s8BtnRow { bottom: 12%; } html[data-view="mobile"] .scene5text { font-size: 1.5rem; }
html[data-view="mobile"] #followBtn { bottom: 12%; font-size: 1.3rem; padding: 1rem 3rem; } html[data-view="mobile"] .hash-bar { height: 4rem; }
html[data-view="mobile"] #whatIsLabBtn { bottom: 12%; font-size: 1.3rem; padding: 1rem 3rem; } html[data-view="mobile"] #s7BtnRow { bottom: 8%; }
html[data-view="mobile"] #s8BtnRow .btnNext { padding: 1rem 2rem; font-size: 1.3rem; } html[data-view="mobile"] #s8BtnRow { bottom: 8%; }
html[data-view="mobile"] #s7BtnRow .btnNext { padding: 1rem 2rem; font-size: 1.3rem; } html[data-view="mobile"] #followBtn { bottom: 8%; font-size: 1.4rem; padding: 1rem 3rem; }
html[data-view="mobile"] #whatIsLabBtn { bottom: 8%; font-size: 1.4rem; padding: 1rem 3rem; }
html[data-view="mobile"] #s8BtnRow .btnNext { padding: 1rem 2rem; font-size: 1.4rem; }
html[data-view="mobile"] #s7BtnRow .btnNext { padding: 1rem 2rem; font-size: 1.4rem; }
/* Desktop view overrides */
html[data-view="desktop"] .scene { font-size: 1.4rem; padding: 3rem; }
html[data-view="desktop"] .scene4text,
html[data-view="desktop"] .scene5text,
html[data-view="desktop"] .scene7text,
html[data-view="desktop"] .scene8text,
html[data-view="desktop"] .s4visual,
html[data-view="desktop"] .s5visual,
html[data-view="desktop"] .s4tl { max-width: 60rem; }
html[data-view="desktop"] .comp-table { font-size: 1rem; max-width: 60rem; }
html[data-view="desktop"] .comp-table th,
html[data-view="desktop"] .comp-table td { padding: 0.6rem 1rem; }
html[data-view="desktop"] .scene4sub { padding: 3rem 3rem 7rem; }
html[data-view="desktop"] #scene3 { padding: 4rem 3rem; }
html[data-view="desktop"] #scene3Title { font-size: 3.5rem; top: 2.5rem; left: 2.5rem; }
html[data-view="desktop"] #scene3Text,
html[data-view="desktop"] #textContainer { max-width: 60rem; }
html[data-view="desktop"] #textContainer { font-size: 1.6rem; }
html[data-view="desktop"] .hub-title { font-size: 2.5rem; }
html[data-view="desktop"] .callout-box { font-size: 1.3rem; padding: 1.5rem 2rem; }
html[data-view="desktop"] #scene2Line1,
html[data-view="desktop"] #scene2Message2 { font-size: 1.4rem; }
html[data-view="desktop"] .scene3ChoiceBtn { font-size: 1.1rem; padding: 1.2rem 2.5rem; }
html[data-view="desktop"] #meshCanvas,
html[data-view="desktop"] #deskCanvas,
html[data-view="desktop"] #phoneCanvas,
html[data-view="desktop"] #exfilCanvas,
html[data-view="desktop"] #exfilDeskCanvas { max-height: 480px; }
html[data-view="desktop"] .punch-card { font-size: 0.95rem; padding: 1.2rem; }
html[data-view="desktop"] .punch-card .punch-icon { font-size: 1.8rem; }
html[data-view="desktop"] .scene3ChoiceBtn:hover,
html[data-view="desktop"] .btnNext:hover,
html[data-view="desktop"] .hub-btn:hover { box-shadow: 0 0 12px rgba(0,255,0,0.2); }
</style> </style>
</head> </head>
<body> <body>
@@ -1608,11 +1647,7 @@
let introStarted = false; let introStarted = false;
function setView(mode) { function setView(mode) {
if (mode === 'mobile') { document.documentElement.setAttribute('data-view', mode);
document.documentElement.setAttribute('data-view', 'mobile');
} else {
document.documentElement.removeAttribute('data-view');
}
sessionStorage.setItem('signalView', mode); sessionStorage.setItem('signalView', mode);
} }
@@ -1632,8 +1667,21 @@
} }
} }
// Show view selector overlay on every page load // Auto-select mobile view in Android APK, otherwise show view selector
document.getElementById('viewOverlay').classList.add('visible'); const isCapacitor = typeof window.Capacitor !== 'undefined';
const isAndroidApp = isCapacitor || /android/i.test(navigator.userAgent);
if (isAndroidApp) {
setView('mobile');
viewChosen = true;
document.getElementById('switchViewBtn').style.display = 'block';
} else {
document.getElementById('viewOverlay').classList.add('visible');
}
// Matrix rain configuration
const fontSize = window.innerWidth < 480 ? 10 : 14;
let columns;
let drops = [];
// Canvas setup for Matrix rain // Canvas setup for Matrix rain
const canvas = document.getElementById('matrixCanvas'); const canvas = document.getElementById('matrixCanvas');
@@ -1642,14 +1690,17 @@
function resizeCanvas() { function resizeCanvas() {
canvas.width = window.innerWidth; canvas.width = window.innerWidth;
canvas.height = window.innerHeight; canvas.height = window.innerHeight;
if (fontSize) {
columns = Math.floor(canvas.width / fontSize);
drops = Array(columns).fill(0).map(() => Math.floor(Math.random() * canvas.height / fontSize));
}
} }
resizeCanvas(); resizeCanvas();
window.addEventListener('resize', resizeCanvas); window.addEventListener('resize', resizeCanvas);
// Matrix rain configuration // Force an extra resize after fonts/viewport settle
const fontSize = window.innerWidth < 480 ? 10 : 14; setTimeout(resizeCanvas, 100);
let columns = Math.floor(canvas.width / fontSize); setTimeout(resizeCanvas, 500);
let drops = [];
const chars = '0123456789@#$%^&*()'; const chars = '0123456789@#$%^&*()';
const rainDuration = 5000; // 5 seconds const rainDuration = 5000; // 5 seconds
let rainActive = false; let rainActive = false;
@@ -1707,7 +1758,7 @@
if (y > canvas.height && Math.random() > 0.975) { if (y > canvas.height && Math.random() > 0.975) {
drops[i] = 0; drops[i] = 0;
} }
drops[i] += 0.5; drops[i] += isAndroidApp ? 0.25 : 0.5;
} }
// End rain after duration // End rain after duration
@@ -2402,7 +2453,7 @@
html+='<tr><td>US DEBT</td><td>$11.9T</td><td>$35T+</td></tr>'; html+='<tr><td>US DEBT</td><td>$11.9T</td><td>$35T+</td></tr>';
html+='<tr><td>BTC ADOPTION</td><td>NONE</td><td>ETFs, NATIONS, FORTUNE 500</td></tr>'; html+='<tr><td>BTC ADOPTION</td><td>NONE</td><td>ETFs, NATIONS, FORTUNE 500</td></tr>';
html+='<tr><td>GOLD CONFISCATION RISK</td><td>LOW</td><td>REAL (CBDCs RISING)</td></tr>'; html+='<tr><td>GOLD CONFISCATION RISK</td><td>LOW</td><td>REAL (CBDCs RISING)</td></tr>';
html+='</table><div class="punch-row"><div class="punch-card"><span class="punch-icon">🟠</span><span class="punch-text">BTC $100K+<br>WALL STREET OWNS IT</span></div><div class="punch-card"><span class="punch-icon">🥇</span><span class="punch-text">GOLD $3K+<br>STILL THE SAFE HAVEN</span></div><div class="punch-card"><span class="punch-icon">📉</span><span class="punch-text">DOLLAR DOWN<br>70% SINCE 2000</span></div></div>'; html+='</table>';
vis.innerHTML=html; vis.innerHTML=html;
vis.classList.add('visible'); vis.classList.add('visible');
showNextBtn('next4f');showNextBtn('sources4f');showNextBtn('back4f'); showNextBtn('next4f');showNextBtn('sources4f');showNextBtn('back4f');
@@ -5815,6 +5866,23 @@
showTechHub(); showTechHub();
}); });
// Double-click/tap to skip scene (same as Escape on desktop)
if (isAndroidApp) {
let lastTapTime = 0;
document.addEventListener('touchend', (e) => {
const now = Date.now();
if (lastTapTime > 0 && now - lastTapTime < 400) {
e.preventDefault();
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
}
lastTapTime = now;
});
} else {
document.addEventListener('dblclick', () => {
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
});
}
// Keyboard shortcuts for testing // Keyboard shortcuts for testing
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') { if (e.key === 'Escape') {
@@ -5957,12 +6025,7 @@
+'<tr><td>US DEBT</td><td>$11.9T</td><td>$35T+</td></tr>' +'<tr><td>US DEBT</td><td>$11.9T</td><td>$35T+</td></tr>'
+'<tr><td>BTC ADOPTION</td><td>NONE</td><td>ETFs, NATIONS, FORTUNE 500</td></tr>' +'<tr><td>BTC ADOPTION</td><td>NONE</td><td>ETFs, NATIONS, FORTUNE 500</td></tr>'
+'<tr><td>GOLD CONFISCATION RISK</td><td>LOW</td><td>REAL (CBDCs RISING)</td></tr>' +'<tr><td>GOLD CONFISCATION RISK</td><td>LOW</td><td>REAL (CBDCs RISING)</td></tr>'
+'</table>' +'</table>';
+'<div class="punch-row">'
+'<div class="punch-card"><span class="punch-icon">\uD83D\uDFE0</span><span class="punch-text">BTC $100K+<br>WALL STREET OWNS IT</span></div>'
+'<div class="punch-card"><span class="punch-icon">\uD83E\uDD47</span><span class="punch-text">GOLD $3K+<br>STILL THE SAFE HAVEN</span></div>'
+'<div class="punch-card"><span class="punch-icon">\uD83D\uDCC9</span><span class="punch-text">DOLLAR DOWN<br>70% SINCE 2000</span></div>'
+'</div>';
s4.querySelector('.s4visual').classList.add('visible'); s4.querySelector('.s4visual').classList.add('visible');
document.getElementById('next4f').style.cssText = ''; document.getElementById('next4f').style.cssText = '';
showNextBtn('next4f'); showNextBtn('next4f');

Binary file not shown.

View File

@@ -1 +0,0 @@
../index.html

6838
www/index.html Normal file

File diff suppressed because it is too large Load Diff