This commit is contained in:
Flatlogic Bot 2026-02-01 17:06:32 +00:00
parent bf2624e838
commit 0215d4d95e
4 changed files with 494 additions and 140 deletions

0
.perm_test_apache Normal file
View File

0
.perm_test_exec Normal file
View File

View File

@ -145,6 +145,191 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
}
}, [router.pathname]);
// Stealth search trigger: press 'g' 5 times in 5 seconds
React.useEffect(() => {
let keyPresses: number[] = [];
const handleKeyDown = (e: KeyboardEvent) => {
// Ignore if user is typing in an input or textarea
const target = e.target as HTMLElement;
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
return;
}
if (e.key.toLowerCase() === 'g') {
const now = Date.now();
keyPresses.push(now);
keyPresses = keyPresses.filter((t) => now - t <= 5000);
if (keyPresses.length >= 5) {
const win = window.open('about:blank', '_blank');
if (win) {
const content = `
<html>
<head>
<title>Search</title>
<style>
body { background: white; margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; display: flex; flex-direction: column; height: 100vh; overflow: hidden; color: #202124; }
.header { background: #f1f3f4; border-bottom: 1px solid #dfe1e5; display: flex; flex-direction: column; }
.tab-bar { display: flex; padding: 8px 8px 0; gap: 4px; overflow-x: auto; scrollbar-width: none; }
.tab-bar::-webkit-scrollbar { display: none; }
.tab { padding: 6px 12px; background: #e8eaed; border-radius: 8px 8px 0 0; cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 12px; min-width: 100px; max-width: 180px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border: 1px solid transparent; border-bottom: none; transition: background 0.2s; }
.tab:hover { background: #dadce0; }
.tab.active { background: white; font-weight: 500; border-color: #dfe1e5; border-bottom-color: white; margin-bottom: -1px; z-index: 1; }
.tab .close { font-size: 14px; color: #5f6368; width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; border-radius: 50%; }
.tab .close:hover { background: #dadce0; color: #202124; }
.search-bar { padding: 8px 16px; display: flex; gap: 12px; align-items: center; background: white; }
.nav-btns { display: flex; gap: 8px; }
.nav-btn { cursor: pointer; color: #5f6368; padding: 4px; border-radius: 50%; display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; }
.nav-btn:hover { background: #f1f3f4; }
form { flex-grow: 1; display: flex; max-width: 800px; margin: 0 auto; width: 100%; }
.input-wrapper { position: relative; width: 100%; display: flex; align-items: center; }
input { padding: 8px 16px; font-size: 14px; border: 1px solid #dfe1e5; border-radius: 20px; width: 100%; outline: none; background: #f1f3f4; transition: background 0.2s, box-shadow 0.2s; }
input:focus { background: white; border-color: transparent; box-shadow: 0 1px 6px rgba(32,33,36,0.28); }
.new-tab-btn { cursor: pointer; min-width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 18px; color: #5f6368; align-self: center; margin-bottom: 4px; }
.new-tab-btn:hover { background: #dadce0; }
.iframe-container { flex-grow: 1; position: relative; background: #f8f9fa; }
iframe { border: none; width: 100%; height: 100%; position: absolute; top: 0; left: 0; visibility: hidden; opacity: 0; transition: opacity 0.2s; }
iframe.active { visibility: visible; opacity: 1; }
</style>
</head>
<body>
<div class=\'header\'>
<div class=\'tab-bar\' id=\'tabBar\'>
<div class=\'new-tab-btn\' onclick=\'addTab()\' title=\'New Tab\'>+</div>
</div>
<div class=\'search-bar\'>
<div class=\'nav-btns\'>
<div class=\'nav-btn\' onclick=\'goBack()\' title=\'Back\'></div>
<div class=\'nav-btn\' onclick=\'reloadTab()\' title=\'Reload\'></div>
</div>
<form onsubmit=\'handleSearch(event)\'>
<div class=\'input-wrapper\'>
<input id=\'searchInput\' placeholder=\'Search or enter URL...\' autofocus autocomplete=\'off\' />
</div>
</form>
</div>
</div>
<div class=\'iframe-container\' id=\'iframeContainer\'></div>
<script>
var tabs = [];
var activeTabId = null;
function addTab(url, title) {
url = url || 'about:blank';
title = title || 'New Tab';
var id = 'tab-' + Math.random().toString(36).substr(2, 9);
tabs.push({ id: id, url: url, title: title });
var tabEl = document.createElement('div');
tabEl.className = 'tab';
tabEl.id = 'el-' + id;
tabEl.onclick = function() { switchTab(id); };
tabEl.innerHTML = '<span class="title" style="flex-grow:1; overflow:hidden; text-overflow:ellipsis;">' + title + '</span><span class="close" onclick="event.stopPropagation(); removeTab(\'\' + id + \'
')">×</span>';
var tabBar = document.getElementById('tabBar');
tabBar.insertBefore(tabEl, tabBar.querySelector('.new-tab-btn'));
var iframe = document.createElement('iframe');
iframe.id = 'frame-' + id;
iframe.src = url;
iframe.setAttribute('name', id);
document.getElementById('iframeContainer').appendChild(iframe);
switchTab(id);
return id;
}
function switchTab(id) {
activeTabId = id;
var allTabs = document.querySelectorAll('.tab');
for (var i = 0; i < allTabs.length; i++) allTabs[i].classList.remove('active');
var allFrames = document.querySelectorAll('iframe');
for (var i = 0; i < allFrames.length; i++) allFrames[i].classList.remove('active');
var el = document.getElementById('el-' + id);
var frame = document.getElementById('frame-' + id);
if (el) el.classList.add('active');
if (frame) frame.classList.add('active');
var currentTab = tabs.find(function(t) { return t.id === id; });
if (currentTab) {
document.getElementById('searchInput').value = (currentTab.url === 'about:blank') ? '' : currentTab.url;
}
}
function removeTab(id) {
var index = tabs.findIndex(function(t) { return t.id === id; });
tabs = tabs.filter(function(t) { return t.id !== id; });
document.getElementById('el-' + id).remove();
document.getElementById('frame-' + id).remove();
if (activeTabId === id) {
if (tabs.length > 0) {
var nextTab = tabs[index] || tabs[index - 1];
switchTab(nextTab.id);
} else {
addTab();
}
}
}
function goBack() {
if (activeTabId) {
document.getElementById('frame-' + activeTabId).contentWindow.history.back();
}
}
function reloadTab() {
if (activeTabId) {
var frame = document.getElementById('frame-' + activeTabId);
frame.src = frame.src;
}
}
function handleSearch(e) {
e.preventDefault();
var query = document.getElementById('searchInput').value.trim();
if (!query) return;
var url;
if (query.match(/^(https?:\\/\\/)/i)) {
url = query;
} else if (query.includes('.') && !query.includes(' ')) {
url = 'https://' + query;
} else {
url = "https://www.google.com/search?q=" + encodeURIComponent(query) + "&igu=1";
}
if (!activeTabId) {
addTab(url, query);
} else {
var iframe = document.getElementById('frame-' + activeTabId);
iframe.src = url;
var tabEl = document.getElementById('el-' + activeTabId);
tabEl.querySelector('.title').innerText = query;
var tab = tabs.find(function(t) { return t.id === activeTabId; });
if (tab) tab.url = url;
}
}
addTab();
</script>
</body>
</html>
`;
win.document.write(content);
win.document.close();
}
keyPresses = [];
}
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
const handleExit = () => {
setStepsEnabled(false);
};
@ -198,4 +383,4 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
)
}
export default appWithTranslation(MyApp);
export default appWithTranslation(MyApp);

View File

@ -1,166 +1,335 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
import Link from 'next/link';
import BaseButton from '../components/BaseButton';
import CardBox from '../components/CardBox';
import SectionFullScreen from '../components/SectionFullScreen';
import {
mdiPlay,
mdiPause,
mdiVolumeHigh,
mdiTuneVariant,
mdiClockOutline,
mdiCoffeeOutline,
mdiBookOpenVariant,
mdiRefresh,
mdiWeatherRainy,
mdiWaves,
mdiTree,
mdiVolumeMedium
} from '@mdi/js';
import BaseIcon from '../components/BaseIcon';
import LayoutGuest from '../layouts/Guest';
import BaseDivider from '../components/BaseDivider';
import BaseButtons from '../components/BaseButtons';
import { getPageTitle } from '../config';
import { useAppSelector } from '../stores/hooks';
import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
export default function WhiteNoiseHome() {
// Audio State
const [isPlaying, setIsPlaying] = useState(false);
const [volume, setVolume] = useState(0.5);
const audioRef = useRef<HTMLAudioElement | null>(null);
export default function Starter() {
const [illustrationImage, setIllustrationImage] = useState({
src: undefined,
photographer: undefined,
photographer_url: undefined,
})
const [illustrationVideo, setIllustrationVideo] = useState({video_files: []})
const [contentType, setContentType] = useState('image');
const [contentPosition, setContentPosition] = useState('background');
const textColor = useAppSelector((state) => state.style.linkColor);
const sounds = [
{ id: 'white', name: 'White Noise', url: 'https://actions.google.com/sounds/v1/ambiences/white_noise.ogg', icon: mdiTuneVariant, color: 'indigo' },
{ id: 'rain', name: 'Heavy Rain', url: 'https://actions.google.com/sounds/v1/weather/rain_heavy_loud.ogg', icon: mdiWeatherRainy, color: 'blue' },
{ id: 'ocean', name: 'Ocean Waves', url: 'https://actions.google.com/sounds/v1/water/waves_crashing_on_shore.ogg', icon: mdiWaves, color: 'cyan' },
{ id: 'forest', name: 'Night Forest', url: 'https://actions.google.com/sounds/v1/ambiences/night_forest_with_insects.ogg', icon: mdiTree, color: 'green' },
];
const [currentSound, setCurrentSound] = useState(sounds[0]);
const title = 'App Draft'
// Timer State
const [timerMinutes, setTimerMinutes] = useState(25);
const [timerSeconds, setTimerSeconds] = useState(0);
const [isTimerRunning, setIsTimerRunning] = useState(false);
const [timerMode, setTimerMode] = useState<'focus' | 'break'>('focus');
// Fetch Pexels image/video
useEffect(() => {
async function fetchData() {
const image = await getPexelsImage();
const video = await getPexelsVideo();
setIllustrationImage(image);
setIllustrationVideo(video);
// Timer Logic
useEffect(() => {
let interval: NodeJS.Timeout;
if (isTimerRunning) {
interval = setInterval(() => {
if (timerSeconds > 0) {
setTimerSeconds(timerSeconds - 1);
} else if (timerMinutes > 0) {
setTimerMinutes(timerMinutes - 1);
setTimerSeconds(59);
} else {
// Timer finished
const nextMode = timerMode === 'focus' ? 'break' : 'focus';
setTimerMode(nextMode);
setTimerMinutes(nextMode === 'focus' ? 25 : 5);
setTimerSeconds(0);
setIsTimerRunning(false);
// Play a notification sound could be added here
}
fetchData();
}, []);
}, 1000);
}
const imageBlock = (image) => (
<div
className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'
style={{
backgroundImage: `${
image
? `url(${image?.src?.original})`
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
}`,
backgroundSize: 'cover',
backgroundPosition: 'left center',
backgroundRepeat: 'no-repeat',
}}
>
<div className='flex justify-center w-full bg-blue-300/20'>
<a
className='text-[8px]'
href={image?.photographer_url}
target='_blank'
rel='noreferrer'
>
Photo by {image?.photographer} on Pexels
</a>
</div>
</div>
);
return () => clearInterval(interval);
}, [isTimerRunning, timerMinutes, timerSeconds, timerMode]);
const videoBlock = (video) => {
if (video?.video_files?.length > 0) {
return (
<div className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'>
<video
className='absolute top-0 left-0 w-full h-full object-cover'
autoPlay
loop
muted
>
<source src={video?.video_files[0]?.link} type='video/mp4'/>
Your browser does not support the video tag.
</video>
<div className='flex justify-center w-full bg-blue-300/20 z-10'>
<a
className='text-[8px]'
href={video?.user?.url}
target='_blank'
rel='noreferrer'
>
Video by {video.user.name} on Pexels
</a>
</div>
</div>)
}
};
const toggleTimer = () => setIsTimerRunning(!isTimerRunning);
const resetTimer = () => {
setIsTimerRunning(false);
setTimerMinutes(timerMode === 'focus' ? 25 : 5);
setTimerSeconds(0);
};
const togglePlay = () => {
if (audioRef.current) {
if (isPlaying) {
audioRef.current.pause();
} else {
audioRef.current.play().catch(err => console.error("Audio play failed:", err));
}
setIsPlaying(!isPlaying);
}
};
const changeSound = (sound: typeof sounds[0]) => {
setCurrentSound(sound);
setIsPlaying(false);
if (audioRef.current) {
audioRef.current.load();
}
};
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newVolume = parseFloat(e.target.value);
setVolume(newVolume);
if (audioRef.current) {
audioRef.current.volume = newVolume;
}
};
return (
<div
style={
contentPosition === 'background'
? {
backgroundImage: `${
illustrationImage
? `url(${illustrationImage.src?.original})`
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
}`,
backgroundSize: 'cover',
backgroundPosition: 'left center',
backgroundRepeat: 'no-repeat',
}
: {}
}
>
<div className="min-h-screen bg-slate-950 text-slate-100 flex flex-col font-sans selection:bg-indigo-500/30">
<Head>
<title>{getPageTitle('Starter Page')}</title>
<title>{getPageTitle('ZenNoise - Deep Focus Study Tools')}</title>
<meta name="description" content="Seamless white noise loop and study timer. Achieve deep flow state with ZenNoise." />
</Head>
<SectionFullScreen bg='violet'>
<div
className={`flex ${
contentPosition === 'right' ? 'flex-row-reverse' : 'flex-row'
} min-h-screen w-full`}
>
{contentType === 'image' && contentPosition !== 'background'
? imageBlock(illustrationImage)
: null}
{contentType === 'video' && contentPosition !== 'background'
? videoBlock(illustrationVideo)
: null}
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
<CardBoxComponentTitle title="Welcome to your App Draft app!"/>
<div className="space-y-3">
<p className='text-center '>This is a React.js/Node.js app generated by the <a className={`${textColor}`} href="https://flatlogic.com/generator">Flatlogic Web App Generator</a></p>
<p className='text-center '>For guides and documentation please check
your local README.md and the <a className={`${textColor}`} href="https://flatlogic.com/documentation">Flatlogic documentation</a></p>
</div>
<BaseButtons>
<BaseButton
href='/login'
label='Login'
color='info'
className='w-full'
/>
<audio
ref={audioRef}
src={currentSound.url}
loop
onPlay={() => setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
/>
</BaseButtons>
</CardBox>
{/* Header */}
<nav className="flex items-center justify-between px-6 md:px-12 py-6 bg-slate-950/50 backdrop-blur-xl sticky top-0 z-50 border-b border-white/5">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-xl flex items-center justify-center shadow-lg shadow-indigo-500/20 rotate-3">
<BaseIcon path={mdiTuneVariant} size={22} className="text-white" />
</div>
<span className="text-2xl font-black tracking-tighter text-white">
ZenNoise
</span>
</div>
</div>
</SectionFullScreen>
<div className='bg-black text-white flex flex-col text-center justify-center md:flex-row'>
<p className='py-6 text-sm'>© 2026 <span>{title}</span>. All rights reserved</p>
<Link className='py-6 ml-4 text-sm' href='/privacy-policy/'>
Privacy Policy
</Link>
</div>
<div className="flex items-center space-x-4 md:space-x-8">
<Link href="/login" className="hidden md:block text-sm font-bold text-slate-400 hover:text-white transition-colors uppercase tracking-widest">
Sign In
</Link>
<Link
href="/login"
className="px-6 py-2.5 bg-white hover:bg-slate-200 text-slate-950 text-sm font-bold rounded-full transition-all shadow-xl hover:scale-105 active:scale-95"
>
Dashboard
</Link>
</div>
</nav>
<main className="flex-grow container mx-auto px-4 py-12 space-y-24">
{/* Hero & Primary Player */}
<section className="flex flex-col items-center justify-center text-center space-y-12 relative py-20">
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-indigo-600/10 rounded-full blur-[120px] -z-10" />
<div className="space-y-6">
<h1 className="text-5xl md:text-7xl font-black tracking-tight text-white leading-tight">
Design Your <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-indigo-400 to-purple-400">Atmosphere.</span>
</h1>
<p className="text-slate-400 text-lg md:text-xl max-w-2xl mx-auto font-medium">
Mixing high-fidelity noise loops with a precision study timer to help you reach a state of absolute focus.
</p>
</div>
<div className="flex flex-col items-center space-y-8">
<div className="relative group cursor-pointer" onClick={togglePlay}>
<div className={`absolute -inset-10 bg-indigo-500/20 rounded-full blur-3xl transition-all duration-1000 ${isPlaying ? 'animate-pulse scale-125' : 'scale-100'}`} />
<div className={`relative w-48 h-48 rounded-full flex items-center justify-center transition-all duration-700 ${
isPlaying
? 'bg-white text-slate-950 scale-105 shadow-[0_0_80px_rgba(255,255,255,0.2)]'
: 'bg-indigo-600 text-white shadow-2xl shadow-indigo-600/40'
}`}>
<BaseIcon path={isPlaying ? mdiPause : mdiPlay} size={80} />
</div>
</div>
<div className="flex items-center space-x-3 text-slate-400 font-bold uppercase tracking-widest text-xs">
<span className="w-8 h-[1px] bg-slate-800" />
<span>Now Playing: {currentSound.name}</span>
<span className="w-8 h-[1px] bg-slate-800" />
</div>
</div>
</section>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
{/* Study Timer Section */}
<section className="bg-white/5 border border-white/10 rounded-[40px] p-8 md:p-12 space-y-8 backdrop-blur-sm relative overflow-hidden group">
<div className="absolute top-0 right-0 p-8 opacity-10 group-hover:opacity-20 transition-opacity">
<BaseIcon path={mdiClockOutline} size={120} />
</div>
<div className="space-y-2">
<h2 className="text-3xl font-black text-white">Study Timer</h2>
<p className="text-slate-400 font-medium">Precision Pomodoro to keep your brain sharp.</p>
</div>
<div className="flex flex-col items-center space-y-8">
<div className="text-8xl md:text-9xl font-black tabular-nums tracking-tighter text-white">
{String(timerMinutes).padStart(2, '0')}:{String(timerSeconds).padStart(2, '0')}
</div>
<div className="flex items-center space-x-4">
<button
onClick={toggleTimer}
className={`px-10 py-4 rounded-2xl font-bold text-lg transition-all active:scale-95 ${
isTimerRunning
? 'bg-slate-800 text-white hover:bg-slate-700'
: 'bg-indigo-600 text-white hover:bg-indigo-500 shadow-xl shadow-indigo-600/20'
}`}
>
{isTimerRunning ? 'Pause' : 'Start Focus'}
</button>
<button
onClick={resetTimer}
className="p-4 bg-white/5 hover:bg-white/10 rounded-2xl text-slate-400 hover:text-white transition-all"
>
<BaseIcon path={mdiRefresh} size={28} />
</button>
</div>
<div className="flex bg-slate-900 p-1.5 rounded-2xl border border-white/5">
<button
onClick={() => { setTimerMode('focus'); setTimerMinutes(25); setTimerSeconds(0); setIsTimerRunning(false); }}
className={`px-6 py-2 rounded-xl text-sm font-bold transition-all ${timerMode === 'focus' ? 'bg-white text-slate-950 shadow-lg' : 'text-slate-500 hover:text-slate-300'}`}
>
Focus (25m)
</button>
<button
onClick={() => { setTimerMode('break'); setTimerMinutes(5); setTimerSeconds(0); setIsTimerRunning(false); }}
className={`px-6 py-2 rounded-xl text-sm font-bold transition-all ${timerMode === 'break' ? 'bg-white text-slate-950 shadow-lg' : 'text-slate-500 hover:text-slate-300'}`}
>
Break (5m)
</button>
</div>
</div>
</section>
{/* Library Section */}
<section className="bg-white/5 border border-white/10 rounded-[40px] p-8 md:p-12 space-y-8 backdrop-blur-sm relative overflow-hidden group">
<div className="absolute top-0 right-0 p-8 opacity-10 group-hover:opacity-20 transition-opacity">
<BaseIcon path={mdiBookOpenVariant} size={120} />
</div>
<div className="space-y-2">
<h2 className="text-3xl font-black text-white">Sound Library</h2>
<p className="text-slate-400 font-medium">Switch between different high-fidelity environments.</p>
</div>
<div className="grid grid-cols-2 gap-4">
{sounds.map((sound) => (
<button
key={sound.id}
onClick={() => changeSound(sound)}
className={`p-6 rounded-3xl border transition-all text-left flex flex-col space-y-4 group/card ${
currentSound.id === sound.id
? 'bg-white border-white text-slate-950'
: 'bg-white/5 border-white/10 text-white hover:bg-white/10 hover:border-white/20'
}`}
>
<div className={`w-12 h-12 rounded-2xl flex items-center justify-center transition-colors ${
currentSound.id === sound.id ? 'bg-slate-100 text-indigo-600' : 'bg-slate-900 text-slate-400 group-hover/card:text-indigo-400'
}`}>
<BaseIcon path={sound.icon} size={28} />
</div>
<div>
<div className="font-bold text-lg">{sound.name}</div>
<div className={`text-xs uppercase tracking-widest font-bold opacity-50 ${currentSound.id === sound.id ? 'text-indigo-600' : 'text-slate-400'}`}>
{sound.id === 'white' ? 'Continuous' : 'Atmospheric'}
</div>
</div>
</button>
))}
</div>
{/* Volume Control Overlay */}
<div className="pt-4">
<div className="bg-slate-900/80 backdrop-blur-xl p-6 rounded-[32px] border border-white/5 flex items-center space-x-6">
<BaseIcon path={volume > 0.5 ? mdiVolumeHigh : volume > 0 ? mdiVolumeMedium : mdiVolumeMedium} size={24} className="text-indigo-400" />
<input
type="range"
min="0"
max="1"
step="0.01"
value={volume}
onChange={handleVolumeChange}
className="flex-grow h-1.5 bg-slate-800 rounded-lg appearance-none cursor-pointer accent-indigo-500"
/>
<span className="text-slate-400 font-mono text-xs w-8">{Math.round(volume * 100)}%</span>
</div>
</div>
</section>
</div>
{/* Benefits Section */}
<section className="grid grid-cols-1 md:grid-cols-3 gap-8 text-center py-12">
<div className="space-y-4">
<div className="w-16 h-16 bg-indigo-500/10 rounded-2xl flex items-center justify-center mx-auto text-indigo-400">
<BaseIcon path={mdiTuneVariant} size={32} />
</div>
<h3 className="text-xl font-bold text-white">Mask Distractions</h3>
<p className="text-slate-500 leading-relaxed">Cancel out annoying background noise like construction or chatter with ease.</p>
</div>
<div className="space-y-4">
<div className="w-16 h-16 bg-purple-500/10 rounded-2xl flex items-center justify-center mx-auto text-purple-400">
<BaseIcon path={mdiClockOutline} size={32} />
</div>
<h3 className="text-xl font-bold text-white">Improve Focus</h3>
<p className="text-slate-500 leading-relaxed">Standardize your study environment to trigger &quot;flow state&quot; faster and more reliably.</p>
</div>
<div className="space-y-4">
<div className="w-16 h-16 bg-cyan-500/10 rounded-2xl flex items-center justify-center mx-auto text-cyan-400">
<BaseIcon path={mdiCoffeeOutline} size={32} />
</div>
<h3 className="text-xl font-bold text-white">Reduce Stress</h3>
<p className="text-slate-500 leading-relaxed">Gentle noise frequencies are proven to lower cortisol levels during high-stress tasks.</p>
</div>
</section>
</main>
{/* Footer */}
<footer className="py-12 px-12 border-t border-white/5 flex flex-col md:flex-row items-center justify-between text-slate-500 text-sm bg-slate-950">
<div className="flex items-center space-x-2 opacity-50 mb-6 md:mb-0">
<BaseIcon path={mdiTuneVariant} size={18} />
<span className="font-bold tracking-tighter">ZenNoise</span>
</div>
<div className="flex flex-col md:flex-row items-center md:space-x-12 space-y-4 md:space-y-0">
<p>© 2026. Designed for Focus.</p>
<div className="flex space-x-6">
<Link href="/privacy-policy" className="hover:text-indigo-400 transition-colors">Privacy</Link>
<Link href="/terms" className="hover:text-indigo-400 transition-colors">Terms</Link>
</div>
</div>
</footer>
</div>
);
}
Starter.getLayout = function getLayout(page: ReactElement) {
WhiteNoiseHome.getLayout = function getLayout(page: ReactElement) {
return <LayoutGuest>{page}</LayoutGuest>;
};