+{article.points} pts · Tier {article.tier}
{article.url && · click to open ↗}
))}
);
}
// ─── Survive Tab ──────────────────────────────────────────────────────────────
function SurviveTab() {
const STEPS = [
{ time: '0s', text: 'See the flash. Abnormally intense light — brighter than anything natural. Do NOT look at it. Avert your eyes immediately.' },
{ time: '0–8s', text: 'Drop immediately. Get below window level. Move toward an interior room NOW. Every second counts — do not stop to gather belongings.' },
{ time: '8s', text: 'Reach a bathroom or hallway. Any interior room with no exterior windows is your target. Bathrooms offer a tub for additional cover.' },
{ time: '8–18s', text: 'Get in the tub or crouch low against an interior wall. Face down, hands covering the back of your neck and head. Stay as low as possible.' },
{ time: '~18–20s', text: 'Shockwave hits. Violent shaking, a massive pressure wave, possible structural movement. Stay covered. Do not move until it passes.' },
{ time: '20s+', text: 'Stay sheltered. Do NOT go outside. Do NOT look out windows. Fallout begins arriving within 15–30 minutes of the detonation.' },
{ time: '15–30min', text: 'Fallout arrives. Wet a cloth — any liquid, any fabric. An energy drink on a sweater works. Slightly damp is better than soaking wet. Cover your nose and mouth.' },
{ time: '24–48hrs', text: 'Remain inside. The most dangerous short-lived radioactive isotopes decay rapidly in the first 24 hours. Staying inside dramatically reduces your total exposure.' },
];
const DISTANCE_TABLE = [
{ dist: '0–0.5 mi', zone: 'Fireball', survival: 'None', note: 'Vaporization zone' },
{ dist: '0.5–1 mi', zone: 'Severe blast', survival: 'Very low', note: 'Total structural collapse' },
{ dist: '1–3 mi', zone: 'Heavy damage', survival: 'Low', note: 'Severe burns, debris' },
{ dist: '3–5 mi', zone: 'Moderate damage', survival: 'Moderate', note: '~18–20s warning window' },
{ dist: '5–10 mi', zone: 'Light damage', survival: 'High', note: 'Shelter-in-place effective' },
{ dist: '10+ mi', zone: 'Fallout risk only', survival: 'Very high', note: 'Shelter + filter critical' },
];
return (
{/* Hero warning */}
READ THIS NOW — NOT WHEN IT HAPPENS
If you live near a military installation, major city, or nuclear facility — you have seconds, not minutes.
The information below is based on established nuclear survival research. Knowing it in advance is the difference.
{/* Flash → Action sequence */}
FLASH → ACTION SEQUENCE
{STEPS.map((step, i) => (
{step.time}
{step.text}
))}
{/* Why 18 seconds */}
WHY YOU HAVE 18 SECONDS
The shockwave from a nuclear detonation travels at approximately 1,100 feet per second — roughly the speed of sound.
At 4 miles from the detonation point, you have roughly 18–20 seconds between the flash and shockwave impact. At 2 miles, that drops to 9 seconds. At 6 miles, you have nearly 30 seconds.
The thermal pulse (the flash) travels at the speed of light. It arrives essentially instantaneously. That flash is your only warning. There is no siren, no announcement — just light.
The window exists because physics. Use it.
{/* Improvised filter */}
IMPROVISED FALLOUT FILTER
Any cloth + any liquid = a fallout particle filter.
Fallout is radioactive dust — physical particles. The immediate danger is inhaling or ingesting these particles, causing internal contamination. A wet cloth over your nose and mouth physically blocks most particles from reaching your lungs.
What works: T-shirt, sock, bandana, paper towel — moistened with water, juice, soda, anything available. Slightly damp works better than soaking wet (soaking can reduce airflow and filtration).
This does not stop gamma radiation penetrating your body from outside. But it prevents internal contamination — your biggest controllable post-blast survival variable.
{/* Distance table */}
DISTANCE SURVIVAL ZONES (STANDARD WARHEAD)
Distance
Zone
Survival
Notes
{DISTANCE_TABLE.map((row, i) => (
{row.dist}
{row.zone}
{row.survival}
{row.note}
))}
Distances are approximate for a 300kt warhead detonated at optimal burst height. Actual effects vary significantly based on yield, burst height, terrain, building construction, and weather. These figures are for general preparedness reference only.
);
}
// ─── About Tab ────────────────────────────────────────────────────────────────
function AboutTab() {
return (
Why I Built StrikeAlert
I'm 18 years old and I live in Albuquerque, New Mexico — 4 miles from Kirtland Air Force Base. Most people don't know that Kirtland holds nearly half of America's entire nuclear arsenal. Around 2,500 warheads are stored underground just miles from a major US city.
I started wondering what would actually happen if a strike occurred. I researched blast radii, thermal pulses, shockwave timing, fallout survival. I learned that at my distance I would have roughly 18 seconds between seeing the flash and the shockwave hitting my building. I learned exactly where to go, what to do, and why a wet cloth matters.
Then I realized — there's no app that gives regular people early warning AND tells them what to actually do. Government alerts fire after decisions have already been made. By then it may be too late to act.
I build web tools and digital products online. So I built StrikeAlert — because the gap between "scared" and "prepared" is just information.
StrikeAlert monitors live news feeds, government sources, and USGS seismic data continuously. The moment indicators rise, you know before the official alert fires.
Built by a solo developer in Albuquerque, NM.
Other tools at [your other sites — add link here]
{/* Tech stack */}
HOW IT WORKS
{[
['Sources', 'BBC, NYT, Reuters, DoD, Sky News, USGS Seismic'],
['Scoring', 'Keyword tier system — 10 / 40 / 100 pts per match'],
['Seismic', 'M4.0+ shallow events near 4 geopolitical zones'],
['Hosting', 'Node.js on Hostinger — strikealert.app'],
['Frontend', 'React PWA — installable, works offline'],
].map(([k, v]) => (
{[
'Priority government & SIGINT sources',
'SMS alerts to your phone',
'Family sharing — up to 5 people',
'Historical threat data & trends',
].map((f, i) => (
{f}
))}
Join the waitlist — launch announcement coming soon
);
}
// ─── Root App ─────────────────────────────────────────────────────────────────
function App() {
const [activeTab, setActiveTab] = useState('DASHBOARD');
const [status, setStatus] = useState(null);
const [flashTrigger, setFlashTrigger] = useState(0);
const [settings, setSettings] = useState(() => {
try {
return JSON.parse(localStorage.getItem('sa-settings') || '{}');
} catch (_) { return {}; }
});
// Default settings
const mergedSettings = {
audioEnabled: true,
notifyOnIncrease: true,
...settings,
};
const prevLevelRef = useRef(null);
// Persist settings
const updateSetting = useCallback((key, val) => {
setSettings(prev => {
const next = { ...prev, [key]: val };
try { localStorage.setItem('sa-settings', JSON.stringify(next)); } catch (_) {}
return next;
});
}, []);
// Fetch status from API
const fetchStatus = useCallback(async () => {
try {
const res = await fetch('/api/status');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
setStatus(data);
const newLevel = data.level;
const prevLevel = prevLevelRef.current;
if (prevLevel !== null && newLevel > prevLevel) {
// Threat level increased
if (mergedSettings.audioEnabled) playAlarm();
if (newLevel === 3) setFlashTrigger(t => t + 1);
// Browser notification
if (mergedSettings.notifyOnIncrease && 'Notification' in window) {
if (Notification.permission === 'granted') {
new Notification('⚠ StrikeAlert — Threat Level Rising', {
body: `${LEVEL_CONFIG[newLevel].label} — ${(data.articles?.[0]?.title || 'Threat indicators rising')}`,
icon: '/icon-192.png',
tag: 'threat-alert',
});
} else if (Notification.permission === 'default') {
Notification.requestPermission();
}
}
}
prevLevelRef.current = newLevel;
} catch (err) {
console.warn('Failed to fetch status:', err);
}
}, [mergedSettings.audioEnabled, mergedSettings.notifyOnIncrease]);
// Initial fetch + polling
useEffect(() => {
fetchStatus();
const t = setInterval(fetchStatus, POLL_INTERVAL_MS);
return () => clearInterval(t);
}, [fetchStatus]);
// Inject/update dynamic accent CSS
const level = status?.level ?? 0;
const accent = LEVEL_CONFIG[level]?.color || '#00ff88';
useEffect(() => {
injectStyles(accent);
// Update meta theme-color
const meta = document.querySelector('meta[name="theme-color"]');
if (meta) meta.setAttribute('content', accent);
}, [accent]);
// Hide boot screen once we have data
useEffect(() => {
if (status !== null) {
const boot = document.getElementById('boot-screen');
if (boot) boot.style.display = 'none';
}
}, [status]);
// SW message listener (background sync)
useEffect(() => {
if (!navigator.serviceWorker) return;
const handler = event => {
if (event.data?.type === 'SYNC_COMPLETE') fetchStatus();
};
navigator.serviceWorker.addEventListener('message', handler);
return () => navigator.serviceWorker.removeEventListener('message', handler);
}, [fetchStatus]);
if (!status) return null; // Boot screen is shown via HTML until data arrives
return (