← Qiki · all runs · 📊 leaderboard
r-1786618138520-2xqw2
Build a habit tracker with weekly streaks, dark mode, keyboard shortcuts, CSV export, and offline localStorage persistence
model /nix/store/w8fajwih8isx2rl94rb8ggc1knmnf8x9-Qwen3.6-35B-A3B-MTP-GGUF upstream http://127.0.0.1:8080 harness 127.0.0.1
score 9 / 10
2026-08-13 10:48:58 UTC · total 150s · 1 step
code-app · done · 150s
Habit Tracker App
9 / 10
model /nix/store/w8fajwih8isx2rl94rb8ggc1knmnf8x9-Qwen3.6-35B-A3B-MTP-GGUF harness 127.0.0.1:8080
validity 2/2
fidelity 2/2
structure 1/2
depth 2/2
cleanliness 2/2
why (1 notes)
- no interactivity (addEventListener/RAF)
app · 373 lines
Click the preview to focus for keyboard input. Open in new tab for full-screen play.
view source (13653 chars)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Streak | Minimalist Habit Tracker</title>
<meta name="description" content="Minimalist habit tracker with weekly streaks, offline persistence, and keyboard-first UX.">
<style>
/*
* STREAK APP CSS
* Theme: Sleek Dark Mode, High Contrast, Neon Accents
* Palette: #0f172a (bg), #38bdf8 (accent), #ffffff (text), #1e293b (surface)
*/
:root {
/* Brand Colors */
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--bg-tertiary: #334155;
--text-primary: #ffffff;
--text-secondary: #94a3b8;
--accent-primary: #38bdf8; /* Sky Blue */
--accent-glow: rgba(56, 189, 248, 0.5);
--danger: #ef4444;
--success: #22c55e;
/* Spacing & Layout */
--container-width: 1000px;
--radius-sm: 6px;
--radius-md: 12px;
--radius-lg: 20px;
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
/* Transitions */
--transition-fast: 0.2s ease;
--transition-med: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Reset & Base */
* { box-sizing: border-box; margin: 0; padding: 0; outline: none; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
min-height: 100vh;
overflow-x: hidden;
transition: background-color var(--transition-med), color var(--transition-med);
}
/* Typography */
h1, h2, h3, h4 { font-weight: 700; letter-spacing: -0.02em; }
h1 { font-size: 2.5rem; margin-bottom: var(--spacing-xs); }
h2 { font-size: 1.5rem; margin-bottom: var(--spacing-md); }
p { color: var(--text-secondary); }
.text-accent { color: var(--accent-primary); text-shadow: 0 0 10px var(--accent-glow); }
.text-xs { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }
/* Layout */
.app-container {
max-width: var(--container-width);
margin: 0 auto;
padding: var(--spacing-lg);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Header */
header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: var(--spacing-lg);
border-bottom: 1px solid var(--bg-secondary);
margin-bottom: var(--spacing-xl);
}
.brand { display: flex; flex-direction: column; }
.brand-sub { font-size: 0.875rem; color: var(--text-secondary); }
.controls { display: flex; gap: var(--spacing-md); align-items: center; }
/* Buttons */
button {
cursor: pointer;
border: none;
background: transparent;
color: var(--text-primary);
font-family: inherit;
transition: all var(--transition-fast);
}
.btn-icon {
padding: var(--spacing-sm);
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
}
.btn-icon:hover { background-color: var(--bg-secondary); color: var(--accent-primary); }
.btn-icon svg { width: 24px; height: 24px; fill: currentColor; }
.btn-primary {
background-color: var(--bg-secondary);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-sm);
font-weight: 600;
display: flex;
align-items: center;
gap: var(--spacing-sm);
border: 1px solid transparent;
}
.btn-primary:hover {
border-color: var(--accent-primary);
box-shadow: 0 0 15px var(--accent-glow);
transform: translateY(-1px);
}
.btn-primary svg { width: 18px; height: 18px; fill: currentColor; }
/* Main Content */
main { flex: 1; }
/* Habit Grid */
.habit-grid {
display: grid;
grid-template-columns: 1fr repeat(7, 1fr);
gap: var(--spacing-md);
align-items: center;
margin-bottom: var(--spacing-lg);
}
/* Headers */
.col-header {
padding: var(--spacing-sm);
text-align: center;
color: var(--text-secondary);
font-size: 0.85rem;
font-weight: 600;
}
.col-header.today { color: var(--accent-primary); }
.col-header-name { text-align: left; }
/* Habit Row */
.habit-row {
background-color: var(--bg-secondary);
border-radius: var(--radius-md);
padding: var(--spacing-md);
display: contents; /* Allows children to participate in grid */
transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
.habit-row:hover { transform: translateY(-2px); box-shadow: 0 4px 20px rgba(0,0,0,0.2); }
.habit-info {
display: flex;
flex-direction: column;
justify-content: center;
}
.habit-name { font-weight: 600; font-size: 1.1rem; margin-bottom: 2px; }
.habit-streak { font-size: 0.8rem; color: var(--text-secondary); display: flex; align-items: center; gap: 4px; }
.streak-fire { color: #f59e0b; }
.habit-actions {
display: flex;
justify-content: flex-end;
opacity: 0;
transition: opacity var(--transition-fast);
}
.habit-row:hover .habit-actions { opacity: 1; }
.btn-delete {
color: var(--text-secondary);
padding: var(--spacing-xs);
}
.btn-delete:hover { color: var(--danger); }
.btn-delete svg { width: 18px; height: 18px; fill: currentColor; }
/* Day Checkboxes */
.day-cell {
display: flex;
justify-content: center;
}
.checkbox-wrapper {
width: 32px;
height: 32px;
border-radius: 50%;
border: 2px solid var(--bg-tertiary);
cursor: pointer;
transition: all var(--transition-med);
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.checkbox-wrapper:hover { border-color: var(--accent-primary); }
.checkbox-wrapper.checked {
background-color: var(--accent-primary);
border-color: var(--accent-primary);
box-shadow: 0 0 12px var(--accent-glow);
}
.checkbox-wrapper.checked::after {
content: '';
width: 8px;
height: 14px;
border: solid var(--bg-primary);
border-width: 0 3px 3px 0;
transform: rotate(45deg) translate(-1px, -1px);
}
/* Keyboard Shortcut Hint */
kbd {
background: var(--bg-tertiary);
padding: 2px 6px;
border-radius: 4px;
font-family: monospace;
font-size: 0.7rem;
margin-left: 8px;
color: var(--text-secondary);
border-bottom: 1px solid #000;
}
/* Modal */
.modal-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(15, 23, 42, 0.85);
backdrop-filter: blur(4px);
z-index: 100;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
pointer-events: none;
transition: opacity var(--transition-med);
}
.modal-overlay.active { opacity: 1; pointer-events: auto; }
.modal {
background: var(--bg-secondary);
padding: var(--spacing-xl);
border-radius: var(--radius-lg);
width: 90%;
max-width: 400px;
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
transform: translateY(20px);
transition: transform var(--transition-med);
border: 1px solid var(--bg-tertiary);
}
.modal-overlay.active .modal { transform: translateY(0); }
.form-group { margin-bottom: var(--spacing-lg); }
.form-label { display: block; margin-bottom: var(--spacing-sm); font-size: 0.9rem; color: var(--text-secondary); }
.form-input {
width: 100%;
background: var(--bg-primary);
border: 1px solid var(--bg-tertiary);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-sm);
color: var(--text-primary);
font-size: 1rem;
}
.form-input:focus { border-color: var(--accent-primary); }
/* Toast Notification */
.toast-container {
position: fixed;
bottom: var(--spacing-lg);
right: var(--spacing-lg);
z-index: 200;
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
}
.toast {
background: var(--bg-secondary);
border-left: 4px solid var(--accent-primary);
padding: var(--spacing-md);
border-radius: var(--radius-sm);
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
animation: slideIn 0.3s ease forwards;
display: flex;
align-items: center;
gap: var(--spacing-md);
min-width: 250px;
}
.toast.success { border-color: var(--success); }
.toast.error { border-color: var(--danger); }
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes fadeOut {
to { transform: translateX(100%); opacity: 0; }
}
/* Empty State */
.empty-state {
text-align: center;
padding: var(--spacing-xl);
color: var(--text-secondary);
border: 2px dashed var(--bg-secondary);
border-radius: var(--radius-lg);
}
.empty-state svg { width: 48px; height: 48px; fill: var(--bg-tertiary); margin-bottom: var(--spacing-md); }
/* Responsive */
@media (max-width: 768px) {
.habit-grid {
grid-template-columns: 1fr repeat(7, 1fr); /* Keep grid for mobile but scrollable container */
overflow-x: auto;
display: block; /* Switch to block for easier scrolling on mobile */
white-space: nowrap;
}
.habit-row {
display: inline-block;
vertical-align: top;
width: auto;
min-width: 280px;
margin-right: var(--spacing-md);
}
.col-header { display: none; } /* Hide headers on mobile, rely on sticky titles */
.habit-info {
margin-bottom: var(--spacing-md);
padding-bottom: var(--spacing-md);
border-bottom: 1px solid var(--bg-tertiary);
white-space: normal;
}
.habit-actions { position: absolute; top: var(--spacing-sm); right: var(--spacing-sm); opacity: 1; }
.day-cell { display: inline-flex; margin-right: 8px; }
/* Make the habit row a relative container for the absolute delete button */
.habit-row { position: relative; }
}
/* Utility */
.hidden { display: none !important; }
</style>
</head>
<body>
<div class="app-container">
<header>
<div class="brand">
<h1>Streak <span class="text-accent">.</span></h1>
<span class="brand-sub">Minimalist habit tracker with offline persistence.</span>
</div>
<div class="controls">
<button id="btn-export" class="btn-icon" title="Export CSV (e)">
<svg viewBox="0 0 24 24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
</button>
<button id="btn-theme" class="btn-icon" title="Toggle Theme">
<!-- Sun Icon -->
<svg id="icon-sun" class="hidden" viewBox="0 0 24 24"><path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58a.996.996 0 00-1.41 0 .996.996 0 000 1.41l1.41 1.41c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37a.996.996 0 00-1.41 0 .996.996 0 000 1.41l1.41 1.41c.39.39 1.03.39 1.41 0a.996.996 0 000-1.41l-1.41-1.41zm1.06-10.96a.996.996 0 000-1.41.99
</body>
</html> 1 LLM call on this step
ok ·
149645 ms · prompt 1688c → resp 13645c ·
t=0.6 max=4096 ·
→ /nix/store/w8fajwih8isx2rl94rb8ggc1knmnf8x9-Qwen3.6-35B-A3B-MTP-GGUF @ http://127.0.0.1:8080
→ /nix/store/w8fajwih8isx2rl94rb8ggc1knmnf8x9-Qwen3.6-35B-A3B-MTP-GGUF @ http://127.0.0.1:8080
raw JSON
{
"id": "r-1786618138520-2xqw2",
"prompt": "Build a habit tracker with weekly streaks, dark mode, keyboard shortcuts, CSV export, and offline localStorage persistence",
"project": {
"name": "Streak",
"tagline": "Minimalist habit tracker with weekly streaks, offline persistence, and keyboard-first UX.",
"theme": "sleek dark-mode interface, high-contrast data visualization, clean sans-serif typography, and subtle neon accents for active streaks",
"brandColors": [
"#0f172a",
"#38bdf8",
"#ffffff",
"#1e293b"
],
"audience": "productivity enthusiasts, developers, and data-driven individuals",
"vibe": "minimal, efficient, focused, modern, reliable"
},
"steps": [
{
"id": "s1-xujkc",
"kind": "code-app",
"params": {
"brief": "Build a fully functional, single-file HTML habit tracker application. Features required: 1. Weekly streak tracking with visual indicators. 2. Dark mode toggle with system preference detection. 3. Keyboard shortcuts (e.g., 'n' for new habit, '1-7' for toggling days, 'e' for export). 4. CSV export functionality for all habit data. 5. Offline persistence using localStorage. 6. Modern, clean UI with smooth transitions. 7. Responsive design for mobile and desktop. Include all CSS and JS inline in a single HTML file.",
"kind": "app",
"project": {
"name": "Streak",
"tagline": "Minimalist habit tracker with weekly streaks, offline persistence, and keyboard-first UX.",
"theme": "sleek dark-mode interface, high-contrast data visualization, clean sans-serif typography, and subtle neon accents for active streaks",
"brandColors": [
"#0f172a",
"#38bdf8",
"#ffffff",
"#1e293b"
],
"audience": "productivity enthusiasts, developers, and data-driven individuals",
"vibe": "minimal, efficient, focused, modern, reliable"
},
"heroPlanned": false
},
"label": "Habit Tracker App"
}
],
"results": {
"s1-xujkc": {
"status": "done",
"startedAt": 1786618138521,
"attempt": 1,
"output": {
"html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Streak | Minimalist Habit Tracker</title>\n <meta name=\"description\" content=\"Minimalist habit tracker with weekly streaks, offline persistence, and keyboard-first UX.\">\n <style>\n /* \n * STREAK APP CSS\n * Theme: Sleek Dark Mode, High Contrast, Neon Accents\n * Palette: #0f172a (bg), #38bdf8 (accent), #ffffff (text), #1e293b (surface)\n */\n\n :root {\n /* Brand Colors */\n --bg-primary: #0f172a;\n --bg-secondary: #1e293b;\n --bg-tertiary: #334155;\n --text-primary: #ffffff;\n --text-secondary: #94a3b8;\n --accent-primary: #38bdf8; /* Sky Blue */\n --accent-glow: rgba(56, 189, 248, 0.5);\n --danger: #ef4444;\n --success: #22c55e;\n \n /* Spacing & Layout */\n --container-width: 1000px;\n --radius-sm: 6px;\n --radius-md: 12px;\n --radius-lg: 20px;\n --spacing-xs: 4px;\n --spacing-sm: 8px;\n --spacing-md: 16px;\n --spacing-lg: 24px;\n --spacing-xl: 32px;\n\n /* Transitions */\n --transition-fast: 0.2s ease;\n --transition-med: 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n /* Reset & Base */\n * { box-sizing: border-box; margin: 0; padding: 0; outline: none; }\n \n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n background-color: var(--bg-primary);\n color: var(--text-primary);\n line-height: 1.6;\n min-height: 100vh;\n overflow-x: hidden;\n transition: background-color var(--transition-med), color var(--transition-med);\n }\n\n /* Typography */\n h1, h2, h3, h4 { font-weight: 700; letter-spacing: -0.02em; }\n h1 { font-size: 2.5rem; margin-bottom: var(--spacing-xs); }\n h2 { font-size: 1.5rem; margin-bottom: var(--spacing-md); }\n p { color: var(--text-secondary); }\n \n .text-accent { color: var(--accent-primary); text-shadow: 0 0 10px var(--accent-glow); }\n .text-xs { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }\n\n /* Layout */\n .app-container {\n max-width: var(--container-width);\n margin: 0 auto;\n padding: var(--spacing-lg);\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n }\n\n /* Header */\n header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: var(--spacing-lg);\n border-bottom: 1px solid var(--bg-secondary);\n margin-bottom: var(--spacing-xl);\n }\n\n .brand { display: flex; flex-direction: column; }\n .brand-sub { font-size: 0.875rem; color: var(--text-secondary); }\n\n .controls { display: flex; gap: var(--spacing-md); align-items: center; }\n\n /* Buttons */\n button {\n cursor: pointer;\n border: none;\n background: transparent;\n color: var(--text-primary);\n font-family: inherit;\n transition: all var(--transition-fast);\n }\n\n .btn-icon {\n padding: var(--spacing-sm);\n border-radius: var(--radius-sm);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .btn-icon:hover { background-color: var(--bg-secondary); color: var(--accent-primary); }\n .btn-icon svg { width: 24px; height: 24px; fill: currentColor; }\n\n .btn-primary {\n background-color: var(--bg-secondary);\n padding: var(--spacing-sm) var(--spacing-md);\n border-radius: var(--radius-sm);\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n border: 1px solid transparent;\n }\n .btn-primary:hover {\n border-color: var(--accent-primary);\n box-shadow: 0 0 15px var(--accent-glow);\n transform: translateY(-1px);\n }\n .btn-primary svg { width: 18px; height: 18px; fill: currentColor; }\n\n /* Main Content */\n main { flex: 1; }\n\n /* Habit Grid */\n .habit-grid {\n display: grid;\n grid-template-columns: 1fr repeat(7, 1fr);\n gap: var(--spacing-md);\n align-items: center;\n margin-bottom: var(--spacing-lg);\n }\n\n /* Headers */\n .col-header {\n padding: var(--spacing-sm);\n text-align: center;\n color: var(--text-secondary);\n font-size: 0.85rem;\n font-weight: 600;\n }\n .col-header.today { color: var(--accent-primary); }\n .col-header-name { text-align: left; }\n\n /* Habit Row */\n .habit-row {\n background-color: var(--bg-secondary);\n border-radius: var(--radius-md);\n padding: var(--spacing-md);\n display: contents; /* Allows children to participate in grid */\n transition: transform var(--transition-fast), box-shadow var(--transition-fast);\n }\n \n .habit-row:hover { transform: translateY(-2px); box-shadow: 0 4px 20px rgba(0,0,0,0.2); }\n\n .habit-info {\n display: flex;\n flex-direction: column;\n justify-content: center;\n }\n \n .habit-name { font-weight: 600; font-size: 1.1rem; margin-bottom: 2px; }\n .habit-streak { font-size: 0.8rem; color: var(--text-secondary); display: flex; align-items: center; gap: 4px; }\n .streak-fire { color: #f59e0b; }\n \n .habit-actions {\n display: flex;\n justify-content: flex-end;\n opacity: 0;\n transition: opacity var(--transition-fast);\n }\n .habit-row:hover .habit-actions { opacity: 1; }\n \n .btn-delete {\n color: var(--text-secondary);\n padding: var(--spacing-xs);\n }\n .btn-delete:hover { color: var(--danger); }\n .btn-delete svg { width: 18px; height: 18px; fill: currentColor; }\n\n /* Day Checkboxes */\n .day-cell {\n display: flex;\n justify-content: center;\n }\n\n .checkbox-wrapper {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: 2px solid var(--bg-tertiary);\n cursor: pointer;\n transition: all var(--transition-med);\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .checkbox-wrapper:hover { border-color: var(--accent-primary); }\n \n .checkbox-wrapper.checked {\n background-color: var(--accent-primary);\n border-color: var(--accent-primary);\n box-shadow: 0 0 12px var(--accent-glow);\n }\n\n .checkbox-wrapper.checked::after {\n content: '';\n width: 8px;\n height: 14px;\n border: solid var(--bg-primary);\n border-width: 0 3px 3px 0;\n transform: rotate(45deg) translate(-1px, -1px);\n }\n\n /* Keyboard Shortcut Hint */\n kbd {\n background: var(--bg-tertiary);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 0.7rem;\n margin-left: 8px;\n color: var(--text-secondary);\n border-bottom: 1px solid #000;\n }\n\n /* Modal */\n .modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(15, 23, 42, 0.85);\n backdrop-filter: blur(4px);\n z-index: 100;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--transition-med);\n }\n .modal-overlay.active { opacity: 1; pointer-events: auto; }\n\n .modal {\n background: var(--bg-secondary);\n padding: var(--spacing-xl);\n border-radius: var(--radius-lg);\n width: 90%;\n max-width: 400px;\n box-shadow: 0 20px 50px rgba(0,0,0,0.5);\n transform: translateY(20px);\n transition: transform var(--transition-med);\n border: 1px solid var(--bg-tertiary);\n }\n .modal-overlay.active .modal { transform: translateY(0); }\n\n .form-group { margin-bottom: var(--spacing-lg); }\n .form-label { display: block; margin-bottom: var(--spacing-sm); font-size: 0.9rem; color: var(--text-secondary); }\n .form-input {\n width: 100%;\n background: var(--bg-primary);\n border: 1px solid var(--bg-tertiary);\n padding: var(--spacing-sm) var(--spacing-md);\n border-radius: var(--radius-sm);\n color: var(--text-primary);\n font-size: 1rem;\n }\n .form-input:focus { border-color: var(--accent-primary); }\n\n /* Toast Notification */\n .toast-container {\n position: fixed;\n bottom: var(--spacing-lg);\n right: var(--spacing-lg);\n z-index: 200;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-sm);\n }\n .toast {\n background: var(--bg-secondary);\n border-left: 4px solid var(--accent-primary);\n padding: var(--spacing-md);\n border-radius: var(--radius-sm);\n box-shadow: 0 5px 15px rgba(0,0,0,0.3);\n animation: slideIn 0.3s ease forwards;\n display: flex;\n align-items: center;\n gap: var(--spacing-md);\n min-width: 250px;\n }\n .toast.success { border-color: var(--success); }\n .toast.error { border-color: var(--danger); }\n \n @keyframes slideIn {\n from { transform: translateX(100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n @keyframes fadeOut {\n to { transform: translateX(100%); opacity: 0; }\n }\n\n /* Empty State */\n .empty-state {\n text-align: center;\n padding: var(--spacing-xl);\n color: var(--text-secondary);\n border: 2px dashed var(--bg-secondary);\n border-radius: var(--radius-lg);\n }\n .empty-state svg { width: 48px; height: 48px; fill: var(--bg-tertiary); margin-bottom: var(--spacing-md); }\n\n /* Responsive */\n @media (max-width: 768px) {\n .habit-grid {\n grid-template-columns: 1fr repeat(7, 1fr); /* Keep grid for mobile but scrollable container */\n overflow-x: auto;\n display: block; /* Switch to block for easier scrolling on mobile */\n white-space: nowrap;\n }\n \n .habit-row {\n display: inline-block;\n vertical-align: top;\n width: auto;\n min-width: 280px;\n margin-right: var(--spacing-md);\n }\n\n .col-header { display: none; } /* Hide headers on mobile, rely on sticky titles */\n \n .habit-info {\n margin-bottom: var(--spacing-md);\n padding-bottom: var(--spacing-md);\n border-bottom: 1px solid var(--bg-tertiary);\n white-space: normal;\n }\n \n .habit-actions { position: absolute; top: var(--spacing-sm); right: var(--spacing-sm); opacity: 1; }\n \n .day-cell { display: inline-flex; margin-right: 8px; }\n \n /* Make the habit row a relative container for the absolute delete button */\n .habit-row { position: relative; }\n }\n\n /* Utility */\n .hidden { display: none !important; }\n </style>\n</head>\n<body>\n\n<div class=\"app-container\">\n <header>\n <div class=\"brand\">\n <h1>Streak <span class=\"text-accent\">.</span></h1>\n <span class=\"brand-sub\">Minimalist habit tracker with offline persistence.</span>\n </div>\n <div class=\"controls\">\n <button id=\"btn-export\" class=\"btn-icon\" title=\"Export CSV (e)\">\n <svg viewBox=\"0 0 24 24\"><path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\"/></svg>\n </button>\n <button id=\"btn-theme\" class=\"btn-icon\" title=\"Toggle Theme\">\n <!-- Sun Icon -->\n <svg id=\"icon-sun\" class=\"hidden\" viewBox=\"0 0 24 24\"><path d=\"M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58a.996.996 0 00-1.41 0 .996.996 0 000 1.41l1.41 1.41c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37a.996.996 0 00-1.41 0 .996.996 0 000 1.41l1.41 1.41c.39.39 1.03.39 1.41 0a.996.996 0 000-1.41l-1.41-1.41zm1.06-10.96a.996.996 0 000-1.41.99\n</body>\n</html>",
"kind": "app",
"lineCount": 373,
"charCount": 13653,
"llmAuthored": true
},
"produces": {
"appHtml": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Streak | Minimalist Habit Tracker</title>\n <meta name=\"description\" content=\"Minimalist habit tracker with weekly streaks, offline persistence, and keyboard-first UX.\">\n <style>\n /* \n * STREAK APP CSS\n * Theme: Sleek Dark Mode, High Contrast, Neon Accents\n * Palette: #0f172a (bg), #38bdf8 (accent), #ffffff (text), #1e293b (surface)\n */\n\n :root {\n /* Brand Colors */\n --bg-primary: #0f172a;\n --bg-secondary: #1e293b;\n --bg-tertiary: #334155;\n --text-primary: #ffffff;\n --text-secondary: #94a3b8;\n --accent-primary: #38bdf8; /* Sky Blue */\n --accent-glow: rgba(56, 189, 248, 0.5);\n --danger: #ef4444;\n --success: #22c55e;\n \n /* Spacing & Layout */\n --container-width: 1000px;\n --radius-sm: 6px;\n --radius-md: 12px;\n --radius-lg: 20px;\n --spacing-xs: 4px;\n --spacing-sm: 8px;\n --spacing-md: 16px;\n --spacing-lg: 24px;\n --spacing-xl: 32px;\n\n /* Transitions */\n --transition-fast: 0.2s ease;\n --transition-med: 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n /* Reset & Base */\n * { box-sizing: border-box; margin: 0; padding: 0; outline: none; }\n \n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n background-color: var(--bg-primary);\n color: var(--text-primary);\n line-height: 1.6;\n min-height: 100vh;\n overflow-x: hidden;\n transition: background-color var(--transition-med), color var(--transition-med);\n }\n\n /* Typography */\n h1, h2, h3, h4 { font-weight: 700; letter-spacing: -0.02em; }\n h1 { font-size: 2.5rem; margin-bottom: var(--spacing-xs); }\n h2 { font-size: 1.5rem; margin-bottom: var(--spacing-md); }\n p { color: var(--text-secondary); }\n \n .text-accent { color: var(--accent-primary); text-shadow: 0 0 10px var(--accent-glow); }\n .text-xs { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }\n\n /* Layout */\n .app-container {\n max-width: var(--container-width);\n margin: 0 auto;\n padding: var(--spacing-lg);\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n }\n\n /* Header */\n header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: var(--spacing-lg);\n border-bottom: 1px solid var(--bg-secondary);\n margin-bottom: var(--spacing-xl);\n }\n\n .brand { display: flex; flex-direction: column; }\n .brand-sub { font-size: 0.875rem; color: var(--text-secondary); }\n\n .controls { display: flex; gap: var(--spacing-md); align-items: center; }\n\n /* Buttons */\n button {\n cursor: pointer;\n border: none;\n background: transparent;\n color: var(--text-primary);\n font-family: inherit;\n transition: all var(--transition-fast);\n }\n\n .btn-icon {\n padding: var(--spacing-sm);\n border-radius: var(--radius-sm);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .btn-icon:hover { background-color: var(--bg-secondary); color: var(--accent-primary); }\n .btn-icon svg { width: 24px; height: 24px; fill: currentColor; }\n\n .btn-primary {\n background-color: var(--bg-secondary);\n padding: var(--spacing-sm) var(--spacing-md);\n border-radius: var(--radius-sm);\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n border: 1px solid transparent;\n }\n .btn-primary:hover {\n border-color: var(--accent-primary);\n box-shadow: 0 0 15px var(--accent-glow);\n transform: translateY(-1px);\n }\n .btn-primary svg { width: 18px; height: 18px; fill: currentColor; }\n\n /* Main Content */\n main { flex: 1; }\n\n /* Habit Grid */\n .habit-grid {\n display: grid;\n grid-template-columns: 1fr repeat(7, 1fr);\n gap: var(--spacing-md);\n align-items: center;\n margin-bottom: var(--spacing-lg);\n }\n\n /* Headers */\n .col-header {\n padding: var(--spacing-sm);\n text-align: center;\n color: var(--text-secondary);\n font-size: 0.85rem;\n font-weight: 600;\n }\n .col-header.today { color: var(--accent-primary); }\n .col-header-name { text-align: left; }\n\n /* Habit Row */\n .habit-row {\n background-color: var(--bg-secondary);\n border-radius: var(--radius-md);\n padding: var(--spacing-md);\n display: contents; /* Allows children to participate in grid */\n transition: transform var(--transition-fast), box-shadow var(--transition-fast);\n }\n \n .habit-row:hover { transform: translateY(-2px); box-shadow: 0 4px 20px rgba(0,0,0,0.2); }\n\n .habit-info {\n display: flex;\n flex-direction: column;\n justify-content: center;\n }\n \n .habit-name { font-weight: 600; font-size: 1.1rem; margin-bottom: 2px; }\n .habit-streak { font-size: 0.8rem; color: var(--text-secondary); display: flex; align-items: center; gap: 4px; }\n .streak-fire { color: #f59e0b; }\n \n .habit-actions {\n display: flex;\n justify-content: flex-end;\n opacity: 0;\n transition: opacity var(--transition-fast);\n }\n .habit-row:hover .habit-actions { opacity: 1; }\n \n .btn-delete {\n color: var(--text-secondary);\n padding: var(--spacing-xs);\n }\n .btn-delete:hover { color: var(--danger); }\n .btn-delete svg { width: 18px; height: 18px; fill: currentColor; }\n\n /* Day Checkboxes */\n .day-cell {\n display: flex;\n justify-content: center;\n }\n\n .checkbox-wrapper {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: 2px solid var(--bg-tertiary);\n cursor: pointer;\n transition: all var(--transition-med);\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .checkbox-wrapper:hover { border-color: var(--accent-primary); }\n \n .checkbox-wrapper.checked {\n background-color: var(--accent-primary);\n border-color: var(--accent-primary);\n box-shadow: 0 0 12px var(--accent-glow);\n }\n\n .checkbox-wrapper.checked::after {\n content: '';\n width: 8px;\n height: 14px;\n border: solid var(--bg-primary);\n border-width: 0 3px 3px 0;\n transform: rotate(45deg) translate(-1px, -1px);\n }\n\n /* Keyboard Shortcut Hint */\n kbd {\n background: var(--bg-tertiary);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 0.7rem;\n margin-left: 8px;\n color: var(--text-secondary);\n border-bottom: 1px solid #000;\n }\n\n /* Modal */\n .modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(15, 23, 42, 0.85);\n backdrop-filter: blur(4px);\n z-index: 100;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--transition-med);\n }\n .modal-overlay.active { opacity: 1; pointer-events: auto; }\n\n .modal {\n background: var(--bg-secondary);\n padding: var(--spacing-xl);\n border-radius: var(--radius-lg);\n width: 90%;\n max-width: 400px;\n box-shadow: 0 20px 50px rgba(0,0,0,0.5);\n transform: translateY(20px);\n transition: transform var(--transition-med);\n border: 1px solid var(--bg-tertiary);\n }\n .modal-overlay.active .modal { transform: translateY(0); }\n\n .form-group { margin-bottom: var(--spacing-lg); }\n .form-label { display: block; margin-bottom: var(--spacing-sm); font-size: 0.9rem; color: var(--text-secondary); }\n .form-input {\n width: 100%;\n background: var(--bg-primary);\n border: 1px solid var(--bg-tertiary);\n padding: var(--spacing-sm) var(--spacing-md);\n border-radius: var(--radius-sm);\n color: var(--text-primary);\n font-size: 1rem;\n }\n .form-input:focus { border-color: var(--accent-primary); }\n\n /* Toast Notification */\n .toast-container {\n position: fixed;\n bottom: var(--spacing-lg);\n right: var(--spacing-lg);\n z-index: 200;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-sm);\n }\n .toast {\n background: var(--bg-secondary);\n border-left: 4px solid var(--accent-primary);\n padding: var(--spacing-md);\n border-radius: var(--radius-sm);\n box-shadow: 0 5px 15px rgba(0,0,0,0.3);\n animation: slideIn 0.3s ease forwards;\n display: flex;\n align-items: center;\n gap: var(--spacing-md);\n min-width: 250px;\n }\n .toast.success { border-color: var(--success); }\n .toast.error { border-color: var(--danger); }\n \n @keyframes slideIn {\n from { transform: translateX(100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n @keyframes fadeOut {\n to { transform: translateX(100%); opacity: 0; }\n }\n\n /* Empty State */\n .empty-state {\n text-align: center;\n padding: var(--spacing-xl);\n color: var(--text-secondary);\n border: 2px dashed var(--bg-secondary);\n border-radius: var(--radius-lg);\n }\n .empty-state svg { width: 48px; height: 48px; fill: var(--bg-tertiary); margin-bottom: var(--spacing-md); }\n\n /* Responsive */\n @media (max-width: 768px) {\n .habit-grid {\n grid-template-columns: 1fr repeat(7, 1fr); /* Keep grid for mobile but scrollable container */\n overflow-x: auto;\n display: block; /* Switch to block for easier scrolling on mobile */\n white-space: nowrap;\n }\n \n .habit-row {\n display: inline-block;\n vertical-align: top;\n width: auto;\n min-width: 280px;\n margin-right: var(--spacing-md);\n }\n\n .col-header { display: none; } /* Hide headers on mobile, rely on sticky titles */\n \n .habit-info {\n margin-bottom: var(--spacing-md);\n padding-bottom: var(--spacing-md);\n border-bottom: 1px solid var(--bg-tertiary);\n white-space: normal;\n }\n \n .habit-actions { position: absolute; top: var(--spacing-sm); right: var(--spacing-sm); opacity: 1; }\n \n .day-cell { display: inline-flex; margin-right: 8px; }\n \n /* Make the habit row a relative container for the absolute delete button */\n .habit-row { position: relative; }\n }\n\n /* Utility */\n .hidden { display: none !important; }\n </style>\n</head>\n<body>\n\n<div class=\"app-container\">\n <header>\n <div class=\"brand\">\n <h1>Streak <span class=\"text-accent\">.</span></h1>\n <span class=\"brand-sub\">Minimalist habit tracker with offline persistence.</span>\n </div>\n <div class=\"controls\">\n <button id=\"btn-export\" class=\"btn-icon\" title=\"Export CSV (e)\">\n <svg viewBox=\"0 0 24 24\"><path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\"/></svg>\n </button>\n <button id=\"btn-theme\" class=\"btn-icon\" title=\"Toggle Theme\">\n <!-- Sun Icon -->\n <svg id=\"icon-sun\" class=\"hidden\" viewBox=\"0 0 24 24\"><path d=\"M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58a.996.996 0 00-1.41 0 .996.996 0 000 1.41l1.41 1.41c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37a.996.996 0 00-1.41 0 .996.996 0 000 1.41l1.41 1.41c.39.39 1.03.39 1.41 0a.996.996 0 000-1.41l-1.41-1.41zm1.06-10.96a.996.996 0 000-1.41.99\n</body>\n</html>",
"appKind": "app",
"siteHtml": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Streak | Minimalist Habit Tracker</title>\n <meta name=\"description\" content=\"Minimalist habit tracker with weekly streaks, offline persistence, and keyboard-first UX.\">\n <style>\n /* \n * STREAK APP CSS\n * Theme: Sleek Dark Mode, High Contrast, Neon Accents\n * Palette: #0f172a (bg), #38bdf8 (accent), #ffffff (text), #1e293b (surface)\n */\n\n :root {\n /* Brand Colors */\n --bg-primary: #0f172a;\n --bg-secondary: #1e293b;\n --bg-tertiary: #334155;\n --text-primary: #ffffff;\n --text-secondary: #94a3b8;\n --accent-primary: #38bdf8; /* Sky Blue */\n --accent-glow: rgba(56, 189, 248, 0.5);\n --danger: #ef4444;\n --success: #22c55e;\n \n /* Spacing & Layout */\n --container-width: 1000px;\n --radius-sm: 6px;\n --radius-md: 12px;\n --radius-lg: 20px;\n --spacing-xs: 4px;\n --spacing-sm: 8px;\n --spacing-md: 16px;\n --spacing-lg: 24px;\n --spacing-xl: 32px;\n\n /* Transitions */\n --transition-fast: 0.2s ease;\n --transition-med: 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n /* Reset & Base */\n * { box-sizing: border-box; margin: 0; padding: 0; outline: none; }\n \n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n background-color: var(--bg-primary);\n color: var(--text-primary);\n line-height: 1.6;\n min-height: 100vh;\n overflow-x: hidden;\n transition: background-color var(--transition-med), color var(--transition-med);\n }\n\n /* Typography */\n h1, h2, h3, h4 { font-weight: 700; letter-spacing: -0.02em; }\n h1 { font-size: 2.5rem; margin-bottom: var(--spacing-xs); }\n h2 { font-size: 1.5rem; margin-bottom: var(--spacing-md); }\n p { color: var(--text-secondary); }\n \n .text-accent { color: var(--accent-primary); text-shadow: 0 0 10px var(--accent-glow); }\n .text-xs { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }\n\n /* Layout */\n .app-container {\n max-width: var(--container-width);\n margin: 0 auto;\n padding: var(--spacing-lg);\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n }\n\n /* Header */\n header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: var(--spacing-lg);\n border-bottom: 1px solid var(--bg-secondary);\n margin-bottom: var(--spacing-xl);\n }\n\n .brand { display: flex; flex-direction: column; }\n .brand-sub { font-size: 0.875rem; color: var(--text-secondary); }\n\n .controls { display: flex; gap: var(--spacing-md); align-items: center; }\n\n /* Buttons */\n button {\n cursor: pointer;\n border: none;\n background: transparent;\n color: var(--text-primary);\n font-family: inherit;\n transition: all var(--transition-fast);\n }\n\n .btn-icon {\n padding: var(--spacing-sm);\n border-radius: var(--radius-sm);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .btn-icon:hover { background-color: var(--bg-secondary); color: var(--accent-primary); }\n .btn-icon svg { width: 24px; height: 24px; fill: currentColor; }\n\n .btn-primary {\n background-color: var(--bg-secondary);\n padding: var(--spacing-sm) var(--spacing-md);\n border-radius: var(--radius-sm);\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n border: 1px solid transparent;\n }\n .btn-primary:hover {\n border-color: var(--accent-primary);\n box-shadow: 0 0 15px var(--accent-glow);\n transform: translateY(-1px);\n }\n .btn-primary svg { width: 18px; height: 18px; fill: currentColor; }\n\n /* Main Content */\n main { flex: 1; }\n\n /* Habit Grid */\n .habit-grid {\n display: grid;\n grid-template-columns: 1fr repeat(7, 1fr);\n gap: var(--spacing-md);\n align-items: center;\n margin-bottom: var(--spacing-lg);\n }\n\n /* Headers */\n .col-header {\n padding: var(--spacing-sm);\n text-align: center;\n color: var(--text-secondary);\n font-size: 0.85rem;\n font-weight: 600;\n }\n .col-header.today { color: var(--accent-primary); }\n .col-header-name { text-align: left; }\n\n /* Habit Row */\n .habit-row {\n background-color: var(--bg-secondary);\n border-radius: var(--radius-md);\n padding: var(--spacing-md);\n display: contents; /* Allows children to participate in grid */\n transition: transform var(--transition-fast), box-shadow var(--transition-fast);\n }\n \n .habit-row:hover { transform: translateY(-2px); box-shadow: 0 4px 20px rgba(0,0,0,0.2); }\n\n .habit-info {\n display: flex;\n flex-direction: column;\n justify-content: center;\n }\n \n .habit-name { font-weight: 600; font-size: 1.1rem; margin-bottom: 2px; }\n .habit-streak { font-size: 0.8rem; color: var(--text-secondary); display: flex; align-items: center; gap: 4px; }\n .streak-fire { color: #f59e0b; }\n \n .habit-actions {\n display: flex;\n justify-content: flex-end;\n opacity: 0;\n transition: opacity var(--transition-fast);\n }\n .habit-row:hover .habit-actions { opacity: 1; }\n \n .btn-delete {\n color: var(--text-secondary);\n padding: var(--spacing-xs);\n }\n .btn-delete:hover { color: var(--danger); }\n .btn-delete svg { width: 18px; height: 18px; fill: currentColor; }\n\n /* Day Checkboxes */\n .day-cell {\n display: flex;\n justify-content: center;\n }\n\n .checkbox-wrapper {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: 2px solid var(--bg-tertiary);\n cursor: pointer;\n transition: all var(--transition-med);\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .checkbox-wrapper:hover { border-color: var(--accent-primary); }\n \n .checkbox-wrapper.checked {\n background-color: var(--accent-primary);\n border-color: var(--accent-primary);\n box-shadow: 0 0 12px var(--accent-glow);\n }\n\n .checkbox-wrapper.checked::after {\n content: '';\n width: 8px;\n height: 14px;\n border: solid var(--bg-primary);\n border-width: 0 3px 3px 0;\n transform: rotate(45deg) translate(-1px, -1px);\n }\n\n /* Keyboard Shortcut Hint */\n kbd {\n background: var(--bg-tertiary);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 0.7rem;\n margin-left: 8px;\n color: var(--text-secondary);\n border-bottom: 1px solid #000;\n }\n\n /* Modal */\n .modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(15, 23, 42, 0.85);\n backdrop-filter: blur(4px);\n z-index: 100;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--transition-med);\n }\n .modal-overlay.active { opacity: 1; pointer-events: auto; }\n\n .modal {\n background: var(--bg-secondary);\n padding: var(--spacing-xl);\n border-radius: var(--radius-lg);\n width: 90%;\n max-width: 400px;\n box-shadow: 0 20px 50px rgba(0,0,0,0.5);\n transform: translateY(20px);\n transition: transform var(--transition-med);\n border: 1px solid var(--bg-tertiary);\n }\n .modal-overlay.active .modal { transform: translateY(0); }\n\n .form-group { margin-bottom: var(--spacing-lg); }\n .form-label { display: block; margin-bottom: var(--spacing-sm); font-size: 0.9rem; color: var(--text-secondary); }\n .form-input {\n width: 100%;\n background: var(--bg-primary);\n border: 1px solid var(--bg-tertiary);\n padding: var(--spacing-sm) var(--spacing-md);\n border-radius: var(--radius-sm);\n color: var(--text-primary);\n font-size: 1rem;\n }\n .form-input:focus { border-color: var(--accent-primary); }\n\n /* Toast Notification */\n .toast-container {\n position: fixed;\n bottom: var(--spacing-lg);\n right: var(--spacing-lg);\n z-index: 200;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-sm);\n }\n .toast {\n background: var(--bg-secondary);\n border-left: 4px solid var(--accent-primary);\n padding: var(--spacing-md);\n border-radius: var(--radius-sm);\n box-shadow: 0 5px 15px rgba(0,0,0,0.3);\n animation: slideIn 0.3s ease forwards;\n display: flex;\n align-items: center;\n gap: var(--spacing-md);\n min-width: 250px;\n }\n .toast.success { border-color: var(--success); }\n .toast.error { border-color: var(--danger); }\n \n @keyframes slideIn {\n from { transform: translateX(100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n @keyframes fadeOut {\n to { transform: translateX(100%); opacity: 0; }\n }\n\n /* Empty State */\n .empty-state {\n text-align: center;\n padding: var(--spacing-xl);\n color: var(--text-secondary);\n border: 2px dashed var(--bg-secondary);\n border-radius: var(--radius-lg);\n }\n .empty-state svg { width: 48px; height: 48px; fill: var(--bg-tertiary); margin-bottom: var(--spacing-md); }\n\n /* Responsive */\n @media (max-width: 768px) {\n .habit-grid {\n grid-template-columns: 1fr repeat(7, 1fr); /* Keep grid for mobile but scrollable container */\n overflow-x: auto;\n display: block; /* Switch to block for easier scrolling on mobile */\n white-space: nowrap;\n }\n \n .habit-row {\n display: inline-block;\n vertical-align: top;\n width: auto;\n min-width: 280px;\n margin-right: var(--spacing-md);\n }\n\n .col-header { display: none; } /* Hide headers on mobile, rely on sticky titles */\n \n .habit-info {\n margin-bottom: var(--spacing-md);\n padding-bottom: var(--spacing-md);\n border-bottom: 1px solid var(--bg-tertiary);\n white-space: normal;\n }\n \n .habit-actions { position: absolute; top: var(--spacing-sm); right: var(--spacing-sm); opacity: 1; }\n \n .day-cell { display: inline-flex; margin-right: 8px; }\n \n /* Make the habit row a relative container for the absolute delete button */\n .habit-row { position: relative; }\n }\n\n /* Utility */\n .hidden { display: none !important; }\n </style>\n</head>\n<body>\n\n<div class=\"app-container\">\n <header>\n <div class=\"brand\">\n <h1>Streak <span class=\"text-accent\">.</span></h1>\n <span class=\"brand-sub\">Minimalist habit tracker with offline persistence.</span>\n </div>\n <div class=\"controls\">\n <button id=\"btn-export\" class=\"btn-icon\" title=\"Export CSV (e)\">\n <svg viewBox=\"0 0 24 24\"><path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\"/></svg>\n </button>\n <button id=\"btn-theme\" class=\"btn-icon\" title=\"Toggle Theme\">\n <!-- Sun Icon -->\n <svg id=\"icon-sun\" class=\"hidden\" viewBox=\"0 0 24 24\"><path d=\"M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58a.996.996 0 00-1.41 0 .996.996 0 000 1.41l1.41 1.41c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37a.996.996 0 00-1.41 0 .996.996 0 000 1.41l1.41 1.41c.39.39 1.03.39 1.41 0a.996.996 0 000-1.41l-1.41-1.41zm1.06-10.96a.996.996 0 000-1.41.99\n</body>\n</html>"
},
"finishedAt": 1786618288166,
"llmCalls": [
{
"upstream": "http://127.0.0.1:8080",
"modelRequested": "txt2txt",
"temperature": 0.6,
"maxTokens": 4096,
"timeoutMs": 360000,
"startedAt": 1786618138521,
"finishedAt": 1786618288166,
"ok": true,
"promptChars": 1688,
"responseChars": 13645,
"modelServed": "/nix/store/w8fajwih8isx2rl94rb8ggc1knmnf8x9-Qwen3.6-35B-A3B-MTP-GGUF"
}
]
}
},
"startedAt": 1786618138520,
"fingerprint": {
"ownAiUrl": "http://127.0.0.1:8080",
"harnessHost": "127.0.0.1",
"modelServed": "qwen-image",
"nodeVersion": "v24.18.1",
"capturedAt": 1786618138520
},
"finishedAt": 1786618288167,
"scores": {
"perStep": {
"s1-xujkc": {
"validity": 2,
"fidelity": 2,
"structure": 1,
"depth": 2,
"cleanliness": 2,
"total": 9,
"notes": [
"no interactivity (addEventListener/RAF)"
]
}
},
"total": 9,
"perKind": {
"code-app": 9
},
"axes": [
"validity",
"fidelity",
"structure",
"depth",
"cleanliness"
]
}
}