diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..9abe3cc
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,180 @@
+
+
+
+
+
+ Chronoception Web Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ Attempt
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
5m 0s
+
Tap to Start
+
+
+
+
+
+
+
+
+
+
+
+
+
Running...
+
00:00
+
Haptics active
+
+
+
+
+
+
+
+
+
+
+
Chronoception Web Demo
+
Interact with the watch screen to simulate the app.
+
+
+
+
+
diff --git a/docs/script.js b/docs/script.js
new file mode 100644
index 0000000..4001d25
--- /dev/null
+++ b/docs/script.js
@@ -0,0 +1,521 @@
+
+// State
+const state = {
+ currentMode: null, // 'challenge', 'fear', 'passive'
+ targetInterval: 300, // seconds (5m)
+ attemptCount: 1, // for challenge/fear
+ repetitionCount: 1, // for passive
+
+ // Session State
+ currentAttemptIndex: 0, // 0-based
+ attempts: [], // Array of { start, tap, error, etc. }
+ passiveRunning: false,
+ passiveTimerId: null,
+ passiveStartTime: null,
+
+ // Settings
+ healthKitEnabled: true,
+ soundEnabled: true
+};
+
+// DOM Elements
+const screens = {
+ home: document.getElementById('screen-home'),
+ picker: document.getElementById('screen-picker'),
+ config: document.getElementById('screen-config'),
+ session: document.getElementById('screen-session'),
+ progress: document.getElementById('screen-progress'),
+ settings: document.getElementById('screen-settings')
+};
+
+// Picker Elements
+const pickerMin = document.getElementById('picker-min');
+const pickerSec = document.getElementById('picker-sec');
+
+// Session Elements
+const sessionChallengeContainer = document.getElementById('session-challenge-container');
+const sessionPassiveContainer = document.getElementById('session-passive-container');
+const challengeTargetDisplay = document.getElementById('challenge-target-display');
+const challengeReady = document.getElementById('challenge-ready');
+const challengeRunning = document.getElementById('challenge-running');
+const challengeFeedback = document.getElementById('challenge-feedback');
+const challengeSummary = document.getElementById('challenge-summary');
+const challengeAttemptLabel = document.getElementById('challenge-attempt-label');
+const feedbackMain = document.getElementById('feedback-main');
+const feedbackSub = document.getElementById('feedback-sub');
+const summaryMeanError = document.getElementById('summary-mean-error');
+const summaryScore = document.getElementById('summary-score');
+const summaryDoneBtn = document.getElementById('summary-done');
+
+const passiveStatus = document.querySelector('.passive-status');
+const passiveTimer = document.getElementById('passive-timer');
+const passiveStopBtn = document.getElementById('passive-stop');
+
+// Init
+function init() {
+ setupMenu();
+ setupPicker();
+ setupConfig();
+ setupSessionInteractions();
+}
+
+// Navigation
+function showScreen(screenName) {
+ Object.values(screens).forEach(s => {
+ s.classList.add('hidden');
+ s.classList.remove('active');
+ });
+ screens[screenName].classList.remove('hidden');
+ screens[screenName].classList.add('active');
+}
+
+// Helper: Format Time
+function formatTime(seconds) {
+ if (seconds < 60) {
+ return `${seconds}s`;
+ }
+ const m = Math.floor(seconds / 60);
+ const s = seconds % 60;
+ return `${m}m ${s}s`;
+}
+
+function formatTimeLong(seconds) {
+ const m = Math.floor(seconds / 60);
+ const s = seconds % 60;
+ return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
+}
+
+// Menu
+function setupMenu() {
+ const buttons = document.querySelectorAll('.menu-item');
+ buttons.forEach(btn => {
+ btn.addEventListener('click', () => {
+ const mode = btn.dataset.mode;
+ if (['challenge', 'fear', 'passive'].includes(mode)) {
+ state.currentMode = mode;
+ showScreen('picker');
+
+ // Restore last used or default to 5m 0s
+ const currentM = Math.floor(state.targetInterval / 60);
+ const currentS = state.targetInterval % 60;
+
+ // Need to wait for display to unhide before scrolling works reliably in some browsers,
+ // but in this simple environment it might be fine.
+ setTimeout(() => {
+ scrollToValue(pickerMin, currentM);
+ scrollToValue(pickerSec, currentS);
+ }, 10);
+
+ } else if (mode === 'progress') {
+ showScreen('progress');
+ } else if (mode === 'settings') {
+ showScreen('settings');
+ setupSettings();
+ }
+ });
+ });
+}
+
+// Picker Logic
+function setupPicker() {
+ // Clear existing
+ pickerMin.innerHTML = '';
+ pickerSec.innerHTML = '';
+
+ pickerMin.innerHTML = ''; // Spacer
+ for (let i = 0; i <= 120; i++) {
+ const div = document.createElement('div');
+ div.className = 'picker-item';
+ div.textContent = i;
+ div.dataset.val = i;
+ pickerMin.appendChild(div);
+ }
+ pickerMin.innerHTML += ''; // Spacer
+
+ pickerSec.innerHTML = ''; // Spacer
+ for (let i = 0; i < 60; i++) {
+ const div = document.createElement('div');
+ div.className = 'picker-item';
+ div.textContent = i;
+ div.dataset.val = i;
+ pickerSec.appendChild(div);
+ }
+ pickerSec.innerHTML += ''; // Spacer
+
+ // Scroll listener
+ pickerMin.addEventListener('scroll', () => highlightSelection(pickerMin));
+ pickerSec.addEventListener('scroll', () => highlightSelection(pickerSec));
+
+ // Initial scroll
+ setTimeout(() => {
+ pickerMin.scrollTop = 5 * 50;
+ pickerSec.scrollTop = 0;
+ }, 10);
+
+ // Confirm button
+ document.getElementById('picker-confirm').addEventListener('click', () => {
+ const m = getSelectedValue(pickerMin);
+ const s = getSelectedValue(pickerSec);
+ state.targetInterval = (m * 60) + s;
+
+ // Validate minimum interval (10s)
+ if (state.targetInterval < 10) {
+ alert("Minimum interval is 10 seconds");
+ return;
+ }
+
+ // Go to config screen
+ updateConfigUI();
+ showScreen('config');
+ });
+}
+
+function scrollToValue(container, value) {
+ container.scrollTop = value * 50;
+}
+
+function getSelectedValue(container) {
+ const itemHeight = 50;
+ const index = Math.round(container.scrollTop / itemHeight);
+ const items = container.querySelectorAll('.picker-item:not([style*="height:50px"])'); // exclude spacers
+ if (items[index]) {
+ return parseInt(items[index].dataset.val);
+ }
+ return 0;
+}
+
+function highlightSelection(container) {
+ const itemHeight = 50;
+ const index = Math.round(container.scrollTop / itemHeight);
+ const items = container.querySelectorAll('.picker-item');
+
+ items.forEach(item => item.classList.remove('selected'));
+ const selectedIndex = index + 1;
+ if (items[selectedIndex]) {
+ items[selectedIndex].classList.add('selected');
+ }
+}
+
+
+// Config Screen
+function setupConfig() {
+ const minus = document.getElementById('config-minus');
+ const plus = document.getElementById('config-plus');
+ const start = document.getElementById('config-start');
+
+ minus.addEventListener('click', () => {
+ if (state.currentMode === 'passive') {
+ // Repetitions
+ if (state.repetitionCount > 1) state.repetitionCount--;
+ } else {
+ // Attempts
+ if (state.attemptCount > 1) state.attemptCount--;
+ }
+ updateConfigUI();
+ });
+
+ plus.addEventListener('click', () => {
+ if (state.currentMode === 'passive') {
+ state.repetitionCount++;
+ } else {
+ state.attemptCount++;
+ }
+ updateConfigUI();
+ });
+
+ start.addEventListener('click', () => {
+ startSession();
+ });
+}
+
+function updateConfigUI() {
+ const title = document.getElementById('config-title');
+ const value = document.getElementById('config-value');
+ const label = document.getElementById('config-label');
+
+ if (state.currentMode === 'passive') {
+ title.textContent = 'Repetitions';
+ value.textContent = state.repetitionCount;
+ label.textContent = state.repetitionCount === 1 ? 'Repetition' : 'Repetitions';
+ } else {
+ // Challenge or Fear
+ title.textContent = 'Attempts';
+ value.textContent = state.attemptCount;
+ label.textContent = state.attemptCount === 1 ? 'Attempt' : 'Attempts';
+ }
+}
+
+// Settings Logic
+function setupSettings() {
+ const toggleHK = document.getElementById('toggle-healthkit');
+ const toggleSound = document.getElementById('toggle-sound');
+
+ // Init state
+ if (state.healthKitEnabled) toggleHK.classList.add('active'); else toggleHK.classList.remove('active');
+ if (state.soundEnabled) toggleSound.classList.add('active'); else toggleSound.classList.remove('active');
+
+ toggleHK.onclick = () => {
+ state.healthKitEnabled = !state.healthKitEnabled;
+ toggleHK.classList.toggle('active');
+ console.log(`HealthKit Enabled: ${state.healthKitEnabled}`);
+ };
+
+ toggleSound.onclick = () => {
+ state.soundEnabled = !state.soundEnabled;
+ toggleSound.classList.toggle('active');
+ console.log(`Sound Enabled: ${state.soundEnabled}`);
+ };
+}
+
+// --- Session Logic ---
+
+function startSession() {
+ showScreen('session');
+
+ if (state.currentMode === 'passive') {
+ startPassiveSession();
+ } else {
+ startChallengeSession(); // Handles both Challenge and Fear
+ }
+}
+
+// Challenge / Fear Mode
+function startChallengeSession() {
+ sessionChallengeContainer.classList.remove('hidden');
+ sessionPassiveContainer.classList.add('hidden');
+
+ // Init State
+ state.currentAttemptIndex = 0;
+ state.attempts = [];
+
+ // Show Ready Screen
+ challengeTargetDisplay.textContent = formatTime(state.targetInterval);
+
+ showChallengeState('ready');
+}
+
+function showChallengeState(s) {
+ // Hide all
+ challengeReady.classList.add('hidden');
+ challengeRunning.classList.add('hidden');
+ challengeFeedback.classList.add('hidden');
+ challengeSummary.classList.add('hidden');
+
+ if (s === 'ready') challengeReady.classList.remove('hidden');
+ if (s === 'running') challengeRunning.classList.remove('hidden');
+ if (s === 'feedback') challengeFeedback.classList.remove('hidden');
+ if (s === 'summary') challengeSummary.classList.remove('hidden');
+}
+
+function setupSessionInteractions() {
+ // Ready -> Start First Attempt
+ challengeReady.addEventListener('click', () => {
+ startAttempt();
+ });
+
+ // Running -> Tap (End Attempt)
+ challengeRunning.addEventListener('click', () => {
+ handleTap();
+ });
+
+ // Summary -> Done (Back to Home)
+ summaryDoneBtn.addEventListener('click', () => {
+ showScreen('home');
+ });
+
+ // Passive Stop
+ passiveStopBtn.addEventListener('click', () => {
+ endPassiveSession();
+ showScreen('home');
+ });
+}
+
+function startAttempt() {
+ const attemptNum = state.currentAttemptIndex + 1;
+ challengeAttemptLabel.textContent = `Attempt ${attemptNum} / ${state.attemptCount}`;
+
+ showChallengeState('running');
+
+ // Record Start Time
+ state.currentAttemptStart = Date.now();
+}
+
+function handleTap() {
+ const tapTime = Date.now();
+ const elapsedMs = tapTime - state.currentAttemptStart;
+ const elapsedSec = elapsedMs / 1000;
+
+ const target = state.targetInterval;
+ const signedError = elapsedSec - target;
+ const absError = Math.abs(signedError);
+ const percentError = signedError / target;
+
+ // Store Attempt Data
+ state.attempts.push({
+ start: state.currentAttemptStart,
+ tap: tapTime,
+ elapsed: elapsedSec,
+ signedError: signedError,
+ absError: absError,
+ percentError: percentError
+ });
+
+ showFeedback(signedError, absError, percentError);
+}
+
+function showFeedback(signedError, absError, percentError) {
+ const isLate = signedError > 0;
+ feedbackMain.textContent = isLate ? "Late" : "Early";
+ feedbackSub.textContent = `by ${formatTime(Math.round(absError))}`;
+
+ // Color Logic based on error zones
+ // Green < 5%, Yellow < 15%, Red >= 15%
+ const absPercent = Math.abs(percentError);
+ let color = '#34c759'; // Green
+ if (absPercent >= 0.15) color = '#ff3b30'; // Red
+ else if (absPercent >= 0.05) color = '#ffcc00'; // Yellow
+
+ feedbackMain.style.color = color;
+
+ showChallengeState('feedback');
+
+ // Fear Mode Check
+ if (state.currentMode === 'fear' && absPercent >= 0.10) {
+ playFearSound();
+ }
+
+ // Auto advance after 2 seconds
+ setTimeout(() => {
+ state.currentAttemptIndex++;
+ if (state.currentAttemptIndex < state.attemptCount) {
+ startAttempt();
+ } else {
+ calculateAndShowSummary();
+ }
+ }, 2000);
+}
+
+function playFearSound() {
+ if (!state.soundEnabled) return;
+ // Simple beep or buzzer simulation
+ // In a real browser, we might use AudioContext.
+ console.log("BZZZZT! Fear Mode Penalty!");
+ // Visual feedback for demo? Flash screen red
+ const originalBg = document.body.style.backgroundColor;
+ document.getElementById('watch-screen').style.backgroundColor = 'red';
+ setTimeout(() => {
+ document.getElementById('watch-screen').style.backgroundColor = 'black';
+ }, 500);
+}
+
+function calculateAndShowSummary() {
+ // Calculate Metrics
+ const totalAbsError = state.attempts.reduce((sum, a) => sum + a.absError, 0);
+ const meanAbsError = totalAbsError / state.attempts.length;
+
+ const totalAbsPercent = state.attempts.reduce((sum, a) => sum + Math.abs(a.percentError), 0);
+ const meanAbsPercent = totalAbsPercent / state.attempts.length;
+
+ // Acuity Score
+ // E_full_scale = 0.50
+ // acuity_raw = 1 - (E / 0.50)
+ // acuity_clamped = max(0, min(1, acuity_raw))
+ // Score = round(100 * acuity_clamped)
+
+ const acuityRaw = 1 - (meanAbsPercent / 0.50);
+ const acuityClamped = Math.max(0, Math.min(1, acuityRaw));
+ const score = Math.round(100 * acuityClamped);
+
+ // Update UI
+ summaryMeanError.textContent = formatTime(Math.round(meanAbsError));
+ summaryScore.textContent = score;
+
+ // Log to "HealthKit"
+ if (state.healthKitEnabled) {
+ console.log("Logging to HealthKit (Mindful Session):", {
+ mode: state.currentMode,
+ interval: state.targetInterval,
+ attempts: state.attempts.length,
+ meanAbsError: meanAbsError,
+ score: score
+ });
+ }
+
+ showChallengeState('summary');
+}
+
+// Passive Mode
+function startPassiveSession() {
+ sessionChallengeContainer.classList.add('hidden');
+ sessionPassiveContainer.classList.remove('hidden');
+
+ state.passiveRunning = true;
+ state.passiveStartTime = Date.now();
+ updatePassiveTimer();
+
+ state.passiveRepetitionsDone = 0;
+
+ // Start Interval Loop
+ schedulePassiveHaptic();
+}
+
+function updatePassiveTimer() {
+ if (!state.passiveRunning) return;
+
+ const now = Date.now();
+ const elapsed = Math.floor((now - state.passiveStartTime) / 1000);
+ passiveTimer.textContent = formatTimeLong(elapsed);
+
+ requestAnimationFrame(updatePassiveTimer);
+}
+
+function schedulePassiveHaptic() {
+ if (!state.passiveRunning) return;
+
+ // In a real app, this would schedule local notifications or run in background.
+ // Here we just use setTimeout for the next boundary.
+ // We need to track how many intervals have passed.
+
+ // Simple simulation: just set timeout for targetInterval
+
+ state.passiveTimerId = setTimeout(() => {
+ triggerHaptic();
+ state.passiveRepetitionsDone++;
+ if (state.passiveRepetitionsDone >= state.repetitionCount) {
+ endPassiveSession();
+ alert("Passive Session Complete");
+ showScreen('home');
+ } else {
+ schedulePassiveHaptic(); // recurse
+ }
+ }, state.targetInterval * 1000);
+}
+
+function triggerHaptic() {
+ console.log("HAPTIC FEEDBACK - Interval Reached");
+ // Visual Pulse
+ const screen = document.getElementById('watch-screen');
+ screen.style.backgroundColor = '#333';
+ setTimeout(() => {
+ screen.style.backgroundColor = 'black';
+ }, 200);
+}
+
+function endPassiveSession() {
+ state.passiveRunning = false;
+ if (state.passiveTimerId) clearTimeout(state.passiveTimerId);
+
+ // Log to HealthKit
+ if (state.healthKitEnabled) {
+ const duration = Math.floor((Date.now() - state.passiveStartTime) / 1000);
+ console.log("Logging to HealthKit (Passive):", {
+ mode: 'passive',
+ interval: state.targetInterval,
+ repetitionCount: state.repetitionCount, // Note: this was the plan, but actual duration might differ if stopped early
+ duration: duration
+ });
+ }
+}
+
+// Run
+init();
diff --git a/docs/style.css b/docs/style.css
new file mode 100644
index 0000000..68bd24e
--- /dev/null
+++ b/docs/style.css
@@ -0,0 +1,482 @@
+:root {
+ --watch-bg: #000000;
+ --watch-text: #ffffff;
+ --accent-color: #ff9500; /* Orange-ish like generic watch apps or maybe blue */
+ --item-bg: #1c1c1e; /* Dark gray for list items */
+ --screen-width: 368px; /* 44mm watch */
+ --screen-height: 448px;
+ --bezel: 20px;
+}
+
+body {
+ background-color: #f0f0f0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+}
+
+.presentation-container {
+ display: flex;
+ gap: 50px;
+ align-items: center;
+}
+
+#watch-case {
+ position: relative;
+ width: calc(var(--screen-width) + var(--bezel) * 2);
+ height: calc(var(--screen-height) + var(--bezel) * 2);
+ background-color: #1a1a1a;
+ border-radius: 50px;
+ box-shadow: 0 20px 50px rgba(0,0,0,0.3);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+#watch-screen {
+ width: var(--screen-width);
+ height: var(--screen-height);
+ background-color: var(--watch-bg);
+ color: var(--watch-text);
+ border-radius: 40px; /* Screen corner radius */
+ overflow: hidden;
+ position: relative;
+ font-size: 18px;
+}
+
+#digital-crown {
+ position: absolute;
+ right: -18px;
+ top: 100px;
+ width: 20px;
+ height: 50px;
+ background: linear-gradient(to right, #333, #666, #333);
+ border-radius: 5px;
+}
+
+#side-button {
+ position: absolute;
+ right: -8px;
+ top: 170px;
+ width: 8px;
+ height: 80px;
+ background-color: #333;
+ border-radius: 4px;
+}
+
+/* Screens */
+.screen {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: 10px;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ transition: opacity 0.3s ease;
+}
+
+.screen.hidden {
+ opacity: 0;
+ pointer-events: none;
+ z-index: -1;
+}
+
+.screen.active {
+ opacity: 1;
+ z-index: 1;
+}
+
+/* Generic Hidden */
+.hidden {
+ display: none !important;
+}
+
+/* Common Elements */
+.header {
+ font-size: 22px;
+ font-weight: 600;
+ margin-top: 10px;
+ margin-bottom: 15px;
+ color: var(--accent-color);
+}
+
+.header-small {
+ font-size: 18px;
+ color: #aaa;
+ margin-top: 5px;
+ margin-bottom: 10px;
+}
+
+/* Menu */
+.menu-list {
+ width: 100%;
+ overflow-y: auto;
+ flex: 1;
+ padding-bottom: 20px;
+}
+
+.menu-item {
+ width: 100%;
+ background-color: var(--item-bg);
+ border: none;
+ border-radius: 10px;
+ padding: 15px;
+ margin-bottom: 8px;
+ display: flex;
+ align-items: center;
+ color: white;
+ text-align: left;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.menu-item:active {
+ background-color: #3a3a3c;
+}
+
+.icon {
+ width: 30px;
+ height: 30px;
+ border-radius: 50%;
+ margin-right: 15px;
+ background-color: gray;
+}
+
+.challenge-icon { background-color: #34c759; } /* Green */
+.fear-icon { background-color: #ff3b30; } /* Red */
+.passive-icon { background-color: #5856d6; } /* Purple */
+.progress-icon { background-color: #ff9500; } /* Orange */
+.settings-icon { background-color: #8e8e93; } /* Gray */
+
+.label {
+ font-weight: 500;
+ font-size: 18px;
+}
+
+/* Picker */
+.picker-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 200px;
+ width: 100%;
+ background-color: #000;
+ position: relative;
+ /* 3D Effect Mask */
+ mask-image: linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%);
+ -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%);
+}
+
+.picker-column {
+ height: 150px;
+ width: 70px;
+ overflow-y: scroll;
+ scroll-snap-type: y mandatory;
+ text-align: center;
+ margin: 0 5px;
+ scroll-behavior: smooth;
+}
+
+/* Hide scrollbar */
+.picker-column::-webkit-scrollbar {
+ display: none;
+}
+
+.picker-item {
+ height: 50px;
+ line-height: 50px;
+ font-size: 28px;
+ scroll-snap-align: center;
+ color: #888;
+}
+
+.picker-item.selected {
+ color: white;
+ font-weight: bold;
+}
+
+.picker-label {
+ font-size: 18px;
+ margin-top: 10px;
+ color: var(--accent-color);
+}
+
+.action-button {
+ background-color: var(--accent-color);
+ color: white;
+ border: none;
+ border-radius: 25px;
+ padding: 12px 40px;
+ font-size: 18px;
+ font-weight: 600;
+ margin-top: 20px;
+ width: 90%;
+ cursor: pointer;
+}
+
+.action-button:active {
+ opacity: 0.8;
+}
+
+.text-button {
+ background: none;
+ border: none;
+ color: #aaa;
+ font-size: 16px;
+ margin-top: 10px;
+ cursor: pointer;
+}
+
+.text-button:active {
+ color: white;
+}
+
+.stop-btn {
+ background-color: #ff3b30;
+}
+
+/* Config Stepper */
+.config-value-display {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+#config-value {
+ font-size: 48px;
+ font-weight: 700;
+ color: white;
+}
+
+#config-label {
+ font-size: 16px;
+ color: #aaa;
+}
+
+.stepper-controls {
+ display: flex;
+ justify-content: space-between;
+ width: 80%;
+ margin-bottom: 20px;
+}
+
+.stepper-btn {
+ width: 50px;
+ height: 50px;
+ border-radius: 25px;
+ background-color: #333;
+ color: white;
+ font-size: 24px;
+ border: none;
+ cursor: pointer;
+}
+
+.stepper-btn:active {
+ background-color: #555;
+}
+
+/* Session Screen styles */
+#session-content {
+ width: 100%;
+ height: 100%;
+}
+
+#session-challenge-container, #session-passive-container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.session-state {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.large-time {
+ font-size: 64px;
+ font-weight: 200;
+ color: white;
+ margin-bottom: 20px;
+}
+
+.large-text {
+ font-size: 36px;
+ font-weight: 500;
+ color: white;
+}
+
+.instruction-text {
+ font-size: 18px;
+ color: var(--accent-color);
+ margin-top: 10px;
+}
+
+/* Feedback */
+#challenge-feedback {
+ background-color: #000; /* will change based on error */
+}
+
+.feedback-main {
+ font-size: 48px;
+ font-weight: 800;
+ margin-bottom: 10px;
+}
+
+.feedback-sub {
+ font-size: 32px;
+ font-weight: 400;
+ color: #ddd;
+}
+
+/* Summary */
+.summary-stat {
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ padding: 10px 20px;
+ border-bottom: 1px solid #333;
+}
+
+.stat-label {
+ color: #aaa;
+}
+
+.stat-value {
+ color: white;
+ font-weight: 600;
+}
+
+.passive-status {
+ font-size: 24px;
+ color: var(--accent-color);
+ margin-bottom: 30px;
+ animation: pulse 2s infinite;
+}
+
+@keyframes pulse {
+ 0% { opacity: 0.5; }
+ 50% { opacity: 1; }
+ 100% { opacity: 0.5; }
+}
+
+/* Progress */
+.progress-list, .settings-list {
+ width: 100%;
+ flex: 1;
+ overflow-y: auto;
+ padding: 10px 0;
+}
+
+.progress-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background-color: var(--item-bg);
+ padding: 15px;
+ border-radius: 10px;
+ margin-bottom: 8px;
+}
+
+.progress-label {
+ color: white;
+ font-size: 18px;
+}
+
+.progress-value {
+ color: var(--accent-color);
+ font-size: 24px;
+ font-weight: 600;
+}
+
+.progress-chart-placeholder {
+ width: 100%;
+ height: 150px;
+ background-color: var(--item-bg);
+ border-radius: 10px;
+ margin-bottom: 15px;
+ display: flex;
+ justify-content: space-around;
+ align-items: flex-end;
+ padding: 10px;
+ box-sizing: border-box;
+}
+
+.chart-bar {
+ width: 15px;
+ background-color: var(--accent-color);
+ border-radius: 3px;
+}
+
+.history-item {
+ display: flex;
+ justify-content: space-between;
+ padding: 10px 15px;
+ border-bottom: 1px solid #333;
+ color: #aaa;
+}
+
+.history-score {
+ color: white;
+ font-weight: bold;
+}
+
+/* Settings */
+.setting-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background-color: var(--item-bg);
+ padding: 15px;
+ border-radius: 10px;
+ margin-bottom: 8px;
+}
+
+.setting-label {
+ color: white;
+ font-size: 18px;
+}
+
+.toggle {
+ width: 50px;
+ height: 30px;
+ background-color: #333;
+ border-radius: 15px;
+ position: relative;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.toggle.active {
+ background-color: #34c759;
+}
+
+.toggle::after {
+ content: '';
+ position: absolute;
+ top: 2px;
+ left: 2px;
+ width: 26px;
+ height: 26px;
+ background-color: white;
+ border-radius: 50%;
+ transition: transform 0.3s;
+}
+
+.toggle.active::after {
+ transform: translateX(20px);
+}