diff --git a/website/CNAME b/website/CNAME new file mode 100644 index 0000000..24cdd26 --- /dev/null +++ b/website/CNAME @@ -0,0 +1 @@ +pausecat.0xarchit.is-a.dev diff --git a/website/assets/Cat_Cursor.png b/website/assets/Cat_Cursor.png new file mode 100644 index 0000000..fe98a7e Binary files /dev/null and b/website/assets/Cat_Cursor.png differ diff --git a/website/assets/Cat_Pointer.png b/website/assets/Cat_Pointer.png new file mode 100644 index 0000000..9f7ffc1 Binary files /dev/null and b/website/assets/Cat_Pointer.png differ diff --git a/website/assets/favicon.svg b/website/assets/favicon.svg new file mode 100644 index 0000000..88659fa --- /dev/null +++ b/website/assets/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/logo.svg b/website/assets/logo.svg new file mode 100644 index 0000000..88659fa --- /dev/null +++ b/website/assets/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/css/animations.css b/website/css/animations.css new file mode 100644 index 0000000..8c75590 --- /dev/null +++ b/website/css/animations.css @@ -0,0 +1,46 @@ +@keyframes heroFloat { + from { transform: translateY(0); } + to { transform: translateY(-12px); } +} + +@keyframes marquee { + from { transform: translateX(0); } + to { transform: translateX(-33.33%); } +} + +@keyframes fadeInUp { + from { opacity: 0; transform: translateY(28px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes textFloat { + 0%,100% { transform: rotateX(20deg) rotateY(-20deg) translateZ(0); } + 50% { transform: rotateX(25deg) rotateY(-15deg) translateZ(40px); } +} + +@keyframes bubbleFadeIn { + from { opacity: 0; transform: scale(0.9) translateY(16px); } + to { opacity: 1; transform: scale(1) translateY(0); } +} + +@keyframes cursorBlink { + 0%,100% { opacity: 1; } + 50% { opacity: 0; } +} + +@keyframes scrollCuePulse { + 0%,100% { opacity: 0.4; transform: scaleY(1); } + 50% { opacity: 0.8; transform: scaleY(1.15); } +} + +/* Scroll reveal classes */ +.reveal { + opacity: 0; + transform: translateY(24px); + transition: opacity 0.55s ease, transform 0.55s ease; +} + +.reveal.revealed { + opacity: 1; + transform: translateY(0); +} \ No newline at end of file diff --git a/website/css/components.css b/website/css/components.css new file mode 100644 index 0000000..f0d8346 --- /dev/null +++ b/website/css/components.css @@ -0,0 +1,222 @@ +/* Navigation */ +nav { + position: fixed; + top: 0; + width: 100%; + height: 64px; + background: rgba(13, 11, 14, 0.82); + backdrop-filter: blur(20px) saturate(160%); + border-bottom: 1px solid var(--border); + z-index: 100; + transition: border-color 0.3s; +} + +nav.scrolled { + border-bottom-color: var(--border-hover); +} + +.nav-content { + display: flex; + align-items: center; + justify-content: space-between; + height: 100%; +} + +.nav-logo { + display: flex; + align-items: center; + gap: 12px; + text-decoration: none; + color: var(--text-primary); + font-size: 1.25rem; + font-weight: 600; +} + +.nav-logo img { + height: 34px; +} + +.nav-links { + display: none; + gap: 24px; +} + +.nav-links a { + text-decoration: none; + color: var(--text-muted); + font-size: 0.8rem; + transition: color 0.2s; +} + +.nav-links a:hover { + color: var(--text-primary); +} + +.nav-actions { + display: none; + align-items: center; + gap: 16px; +} + +@media (min-width: 768px) { + .nav-links { display: flex; } + .nav-actions { display: flex; } +} + +/* Buttons */ +.btn-primary { + display: inline-flex; + align-items: center; + justify-content: center; + background: var(--pink); + color: #0D0B0E; + border-radius: var(--radius-pill); + font-family: 'Syne', sans-serif; + font-weight: 700; + font-size: clamp(0.85rem, 2.5vw, 1rem); + padding: clamp(12px, 3vw, 15px) clamp(24px, 5vw, 40px); + text-decoration: none; + transition: background 0.2s, transform 0.2s, box-shadow 0.2s; +} + +.btn-primary:hover { + background: #f9a8d4; + transform: translateY(-1px); + box-shadow: var(--glow-sm); +} + +.btn-primary.small { + font-size: 0.85rem; + padding: 10px 24px; +} + +.btn-ghost { + display: inline-flex; + align-items: center; + gap: 8px; + font-family: 'Syne', sans-serif; + font-weight: 600; + font-size: 0.9rem; + color: var(--text-secondary); + text-decoration: none; + transition: color 0.2s; +} + +.btn-ghost:hover { + color: var(--text-primary); +} + +.btn-ghost .arrow { + transition: transform 0.2s; +} + +.btn-ghost:hover .arrow { + transform: translateX(3px); +} + +.btn-outline { + display: inline-flex; + align-items: center; + border: 1px solid var(--border); + color: var(--text-secondary); + padding: 8px 16px; + border-radius: var(--radius-sm); + text-decoration: none; + font-size: 0.8rem; + transition: border-color 0.2s, color 0.2s; +} + +.btn-outline:hover { + border-color: var(--border-hover); + color: var(--pink); +} + +/* Badges & Chips */ +.badge-os { + font-size: 0.72rem; + color: var(--text-muted); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + padding: 6px 14px; + display: inline-block; +} + +.chip { + background: var(--bg-surface); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + padding: 6px 12px; + font-family: 'Syne', sans-serif; + font-weight: 500; + font-size: 0.8rem; + color: var(--text-secondary); +} + +.badge-mode-soft { + background: rgba(244,114,182,0.12); + border: 1px solid var(--border-pink); + color: var(--pink); + padding: 4px 10px; + border-radius: var(--radius-pill); +} + +.badge-license { + color: #FCD34D; + border: 1px solid rgba(252,211,77,0.3); + padding: 4px 10px; + border-radius: var(--radius-sm); +} + +code { + background: var(--bg-surface); + color: var(--pink); + padding: 2px 8px; + border-radius: var(--radius-sm); + font-size: 0.85em; +} + +/* Cards */ +.card { + background: var(--bg-surface); + border: 1px solid var(--border); + border-radius: var(--radius-md); + padding: 24px; + transition: border-color 0.3s, box-shadow 0.3s, transform 0.3s; +} + +.card:hover { + border-color: var(--border-hover); + box-shadow: var(--glow-xs); + transform: translateY(-3px); +} + +.card-raised { + background: var(--bg-raised); +} + +/* Stats */ +.stats-strip { + display: inline-flex; + align-items: center; + gap: 16px; + opacity: 0; + animation: fadeInUp 0.6s ease forwards 0.6s; +} + +.stat { + display: flex; + align-items: center; + gap: 6px; +} + +.stat-icon { + font-size: 0.9em; +} + +.stat-value { + color: var(--text-secondary); +} + +.stat-divider { + color: var(--text-muted); +} \ No newline at end of file diff --git a/website/css/layout.css b/website/css/layout.css new file mode 100644 index 0000000..48255ec --- /dev/null +++ b/website/css/layout.css @@ -0,0 +1,56 @@ +.container { + width: 100%; + max-width: var(--max-w); + margin: 0 auto; + padding: 0 1.5rem; +} + +section { + padding: var(--section-pad); +} + +.bg-layer { + background-color: var(--bg-layer); +} + +.bg-surface { + background-color: var(--bg-surface); +} + +/* Utility layout classes */ +.flex { display: flex; } +.flex-col { display: flex; flex-direction: column; } +.items-center { align-items: center; } +.justify-center { justify-content: center; } +.justify-between { justify-content: space-between; } +.gap-2 { gap: 0.5rem; } +.gap-4 { gap: 1rem; } +.gap-6 { gap: 1.5rem; } +.gap-8 { gap: 2rem; } +.gap-12 { gap: 3rem; } + +.grid { display: grid; } +.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } +.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } + +.text-center { text-align: center; } +.text-right { text-align: right; } + +.mx-auto { margin-left: auto; margin-right: auto; } +.mt-8 { margin-top: 2rem; } +.mt-12 { margin-top: 3rem; } +.mb-8 { margin-bottom: 2rem; } +.mb-12 { margin-bottom: 3rem; } + +@media (min-width: 768px) { + .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .md\:flex-row { flex-direction: row; } + .md\:text-left { text-align: left; } +} + +@media (min-width: 1024px) { + .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } +} \ No newline at end of file diff --git a/website/css/main.css b/website/css/main.css new file mode 100644 index 0000000..7c4bc70 --- /dev/null +++ b/website/css/main.css @@ -0,0 +1,676 @@ +@import './variables.css'; +@import './reset.css'; +@import './typography.css'; +@import './layout.css'; +@import './components.css'; +@import './animations.css'; + +/* --- Global Utilities & Overrides --- */ + +/* Scrollbar styling for a consistent dark theme */ +::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +::-webkit-scrollbar-track { + background: var(--bg-void); +} + +::-webkit-scrollbar-thumb { + background: var(--bg-surface); + border-radius: var(--radius-pill); + border: 2px solid var(--bg-void); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); +} + +/* Selection color */ +::selection { + background: var(--pink-glow); + color: var(--text-primary); +} + +/* --- Section Specific Styles --- */ + +/* Hero Specific */ +#hero { + position: relative; + min-height: 100svh; + display: flex; + align-items: center; + justify-content: center; + background-image: repeating-linear-gradient(0deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 48px); +} + +#hero::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -60%); + width: 600px; + height: 400px; + background: radial-gradient(ellipse at center, rgba(244,114,182,0.07) 0%, transparent 70%); + pointer-events: none; + z-index: 0; +} + +.hero-content { + position: relative; + z-index: 1; + max-width: 700px; + display: flex; + flex-direction: column; + align-items: center; +} + +.hero-logo { + height: 160px; + filter: drop-shadow(0 20px 50px rgba(244,114,182,0.3)); + animation: heroFloat 4s ease-in-out infinite alternate; + transition: transform 0.3s ease, filter 0.3s ease; + margin-bottom: 2rem; +} + +@media (max-width: 768px) { + .hero-logo { + height: 120px; + } +} + +.hero-logo:hover { + transform: scale(1.04) translateY(-12px); + filter: drop-shadow(0 25px 60px rgba(244,114,182,0.4)); +} + +.scroll-cue { + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 1px; + height: 60px; + background: linear-gradient(to bottom, var(--pink-border), transparent); + animation: scrollCuePulse 2s ease-in-out infinite; + transform-origin: top; + transition: opacity 0.5s; +} + +.scroll-cue.hidden { + opacity: 0; +} + +/* Ticker Strip */ +.ticker-wrap { + height: 40px; + background: var(--bg-void); + border-top: 1px solid var(--border-pink); + border-bottom: 1px solid var(--border-pink); + overflow: hidden; + display: flex; + align-items: center; +} + +.ticker { + display: flex; + white-space: nowrap; + animation: marquee 26s linear infinite; + font-family: 'Syne', sans-serif; + font-weight: 700; + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--text-muted); +} + +.ticker-item { + padding: 0 2rem; +} + +.ticker-sep { + color: var(--pink); +} + +/* Overlay Preview */ +.overlay-preview-card { + position: relative; + width: 100%; + max-width: 860px; + aspect-ratio: 16/9; + border-radius: var(--radius-lg); + overflow: hidden; + border: 1px solid var(--border); + margin: 0 auto; + container-type: inline-size; +} + +.overlay-layer-0 { + position: absolute; + inset: 0; + background: linear-gradient(135deg, #0A0807 0%, #180D14 40%, #0A0B14 100%); +} + +.overlay-layer-0::after { + content: ''; + position: absolute; + inset: 0; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)' opacity='0.05'/%3E%3C/svg%3E"); + pointer-events: none; +} + +.overlay-layer-1 { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + perspective: 1000px; +} + +.overlay-layer-1-text { + font-family: 'Cormorant Garamond', serif; + font-weight: 700; + font-size: 20cqi; + color: var(--text-primary); + opacity: 0.09; + transform-style: preserve-3d; + animation: textFloat 6s ease-in-out infinite; + text-shadow: 1px 1px 0 rgba(240,234,245,0.4), + 2px 2px 0 rgba(240,234,245,0.4), + 3px 3px 0 rgba(240,234,245,0.4), + 4px 4px 0 rgba(240,234,245,0.4), + 5px 5px 0 rgba(240,234,245,0.4); +} + +.overlay-layer-2 { + position: absolute; + top: 8%; + left: 6%; + width: 23.5cqi; + height: 23.5cqi; + border-radius: 50%; + background: rgba(255, 255, 255, 0.09); + backdrop-filter: blur(28px) saturate(160%); + -webkit-backdrop-filter: blur(28px) saturate(160%); + border: 1px solid rgba(255, 255, 255, 0.18); + box-shadow: 0 32px 64px -12px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.12); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + animation: bubbleFadeIn 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +.bubble-time { + font-family: 'Cormorant Garamond', serif; + font-weight: 600; + font-size: 5.6cqi; + color: var(--text-primary); + line-height: 1; +} + +.bubble-title { + font-family: 'Syne', sans-serif; + font-weight: 600; + font-size: 1.3cqi; + text-transform: uppercase; + letter-spacing: 0.15em; + opacity: 0.75; + margin-top: 0.5cqi; +} + +.bubble-sub { + font-family: 'Syne', sans-serif; + font-weight: 400; + font-size: 1.1cqi; + opacity: 0.45; + margin-top: 0.3cqi; +} + +.bubble-skip { + position: absolute; + bottom: 2.8cqi; + font-family: 'Syne', sans-serif; + font-weight: 600; + font-size: 1.2cqi; + text-transform: uppercase; + letter-spacing: 0.1em; + padding: 0.5cqi 1.4cqi; + border-radius: var(--radius-pill); + border: 1px solid rgba(255,255,255,0.2); + background: transparent; + color: var(--text-primary); + cursor: pointer; + transition: background 0.2s, border-color 0.2s; +} + +.bubble-skip:hover { + background: rgba(255,255,255,0.1); + border-color: rgba(255,255,255,0.4); +} + +.overlay-layer-3 { + position: absolute; + bottom: 1.8cqi; + right: 1.8cqi; +} + +.overlay-layer-3 .badge-mode-soft { + font-size: 1.2cqi; + padding: 0.5cqi 1.2cqi; +} + +/* How It Works */ +.step-item { + border-left: 2px solid var(--border); + padding: 0 0 0 24px; + transition: border-left-color 0.3s; +} + +.step-item:hover { + border-left-color: var(--pink); +} + +.step-number { + font-family: 'Cormorant Garamond', serif; + font-weight: 400; + font-size: 4rem; + color: var(--text-muted); + line-height: 1; + margin-bottom: 1rem; +} + +.step-title { + font-family: 'Cormorant Garamond', serif; + font-weight: 600; + font-size: 1.4rem; + color: var(--text-primary); + margin-bottom: 0.5rem; +} + +.step-connector { + display: none; + flex: 1; + border-top: 1px dashed var(--border); + position: relative; + margin: 0 2rem; + margin-top: 2rem; /* align with number */ +} + +.step-connector::after { + content: '►'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: var(--pink); + font-size: 0.8rem; + background: var(--bg-layer); + padding: 0 4px; +} + +@media (min-width: 768px) { + .steps-row { + display: flex; + align-items: flex-start; + } + .step-connector { + display: block; + } +} + +/* Features */ +.features-grid { + display: grid; + grid-template-columns: 1fr; + gap: 1.5rem; +} + +@media (min-width: 768px) { + .features-grid { + grid-template-columns: repeat(2, 1fr); + } + .md\:col-span-2 { + grid-column: span 2; + } +} + +@media (min-width: 1024px) { + .features-grid { + grid-template-columns: repeat(3, 1fr); + } + .lg\:col-span-2 { + grid-column: span 2; + } + .lg\:col-span-3 { + grid-column: span 3; + } +} + +.feature-icon-area { + font-family: 'Syne', sans-serif; + font-weight: 600; + font-size: 0.7rem; + text-transform: uppercase; + color: var(--pink); + letter-spacing: 0.12em; + margin-bottom: 1rem; +} + +.settings-table { + width: 100%; + margin-top: 1.5rem; + background: var(--bg-raised); + border-radius: var(--radius-sm); + overflow: hidden; + border-left: 2px solid var(--pink); +} + +.settings-table-row { + display: flex; + padding: 8px 12px; + border-bottom: 1px solid var(--border); + font-size: 0.8rem; +} + +.settings-table-row:last-child { + border-bottom: none; +} + +.settings-table-col-1 { + width: 35%; + color: var(--text-primary); +} + +.settings-table-col-2 { + width: 25%; + color: var(--text-primary); + font-family: 'JetBrains Mono', monospace; +} + +.settings-table-col-3 { + width: 40%; + color: var(--text-muted); +} + +/* Modes */ +.modes-divider { + display: none; + width: 1px; + background: var(--border); + position: absolute; + top: 0; + bottom: 0; + left: 50%; + transform: translateX(-50%); +} + +.modes-divider img { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 24px; + height: 24px; + background: var(--bg-base); + padding: 4px; +} + +@media (min-width: 768px) { + .modes-divider { + display: block; + } +} + +.card-hard-mode { + border-color: var(--border-pink); + box-shadow: var(--glow-xs); +} + +/* Open Source */ +.oss-stats { + display: flex; + gap: 1rem; + margin-top: 2rem; + margin-bottom: 2rem; + flex-wrap: wrap; +} + +.oss-stat { + background: var(--bg-raised); + border: 1px solid var(--border); + border-radius: var(--radius-md); + padding: 20px 24px; + flex: 1; + min-width: 140px; +} + +.oss-stat-value { + font-family: 'Cormorant Garamond', serif; + font-weight: 600; + font-size: 3rem; + color: var(--text-primary); + line-height: 1; + margin-bottom: 0.5rem; +} + +.oss-stat-label { + font-family: 'Syne', sans-serif; + font-weight: 600; + font-size: 0.7rem; + text-transform: uppercase; + color: var(--text-muted); +} + +.terminal-card { + background: #080608; + border: 1px solid var(--border); + border-radius: var(--radius-md); + overflow: hidden; + box-shadow: var(--shadow-deep); +} + +.terminal-header { + background: var(--bg-surface); + padding: 8px 16px; + display: flex; + align-items: center; + border-bottom: 1px solid var(--border); +} + +.terminal-dots { + display: flex; + gap: 6px; + margin-right: 16px; +} + +.dot { + width: 10px; + height: 10px; + border-radius: 50%; +} +.dot.red { background: #EF4444; } +.dot.yellow { background: #EAB308; } +.dot.green { background: #22C55E; } + +.terminal-title { + font-family: 'Syne', sans-serif; + font-weight: 500; + font-size: 0.78rem; + color: var(--text-muted); +} + +.terminal-body { + padding: 16px; + font-family: 'JetBrains Mono', monospace; + font-size: 0.82rem; + color: var(--text-secondary); + line-height: 1.5; + min-height: 220px; +} + +.term-prompt { color: var(--pink); } +.term-success { color: #86EFAC; } +.term-cursor { + display: inline-block; + width: 8px; + height: 1em; + background: var(--text-secondary); + vertical-align: middle; + animation: cursorBlink 1s step-end infinite; +} + +/* Install */ +.install-timeline { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.install-step { + display: flex; + gap: 1.5rem; +} + +.install-marker { + display: flex; + flex-direction: column; + align-items: center; +} + +.install-circle { + width: 32px; + height: 32px; + border: 1px solid var(--border-pink); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: var(--pink); + font-family: 'Cormorant Garamond', serif; + font-weight: 600; + font-size: 1.1rem; +} + +.install-line { + width: 1px; + flex: 1; + background: var(--border); + margin-top: 8px; +} + +.req-card { + background: var(--bg-surface); + border-left: 2px solid var(--pink); + border-radius: 0 var(--radius-sm) var(--radius-sm) 0; + padding: 12px 16px; + font-family: 'Syne', sans-serif; + font-weight: 400; + font-size: 0.88rem; + color: var(--text-secondary); +} + +/* Download Section */ +#download { + background: var(--bg-surface); + border-top: 1px solid var(--border-pink); +} + +.btn-large { + font-size: clamp(0.95rem, 3vw, 1.1rem); + padding: clamp(14px, 3.5vw, 18px) clamp(28px, 6vw, 48px); +} + +/* Footer */ +footer { + background: var(--bg-void); + border-top: 1px solid var(--border); + padding: 4rem 1.5rem 2rem; +} + +.footer-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + max-width: var(--max-w); + margin: 0 auto; +} + +@media (min-width: 768px) { + .footer-grid { + grid-template-columns: 2fr 1fr 1fr; + } +} + +.footer-logo { + display: flex; + align-items: center; + gap: 12px; + font-family: 'Cormorant Garamond', serif; + font-weight: 600; + font-size: 1.2rem; + color: var(--text-primary); + text-decoration: none; + margin-bottom: 0.5rem; +} + +.footer-header { + font-family: 'Syne', sans-serif; + font-weight: 700; + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-muted); + margin-bottom: 1rem; +} + +.footer-links { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.footer-links a, .footer-links span { + color: var(--text-muted); + font-size: 0.85rem; + text-decoration: none; + transition: color 0.2s; +} + +.footer-links a:hover { + color: var(--text-primary); +} + +.footer-bottom { + max-width: var(--max-w); + margin: 0 auto; + margin-top: 4rem; + padding-top: 2rem; + border-top: 1px solid var(--border); + text-align: center; + font-size: 0.78rem; + color: var(--text-muted); +} + +/* --- Interactive Cursor & Ambient Lighting --- */ +@media (pointer: fine) { + body { + cursor: url('../assets/Cat_Cursor.png'), auto; + } + + a, button, [role="button"], input, select, textarea, .card, .chip { + cursor: url('../assets/Cat_Pointer.png'), pointer !important; + } + + #ambient-glow { + position: fixed; + inset: 0; + pointer-events: none; + z-index: 9998; + background: radial-gradient(800px circle at var(--mouse-x, 50vw) var(--mouse-y, 50vh), rgba(244, 114, 182, 0.06), transparent 40%); + transition: opacity 0.5s ease; + } +} \ No newline at end of file diff --git a/website/css/reset.css b/website/css/reset.css new file mode 100644 index 0000000..e637720 --- /dev/null +++ b/website/css/reset.css @@ -0,0 +1,57 @@ +*, *::before, *::after { + box-sizing: border-box; +} + +body, h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol { + margin: 0; + padding: 0; +} + +ul[role='list'], ol[role='list'] { + list-style: none; +} + +html:focus-within { + scroll-behavior: smooth; +} + +html, body { + overflow-x: hidden; +} + +body { + min-height: 100vh; + text-rendering: optimizeSpeed; + line-height: 1.5; + background-color: var(--bg-base); + color: var(--text-primary); + -webkit-font-smoothing: antialiased; +} + +a:not([class]) { + text-decoration-skip-ink: auto; +} + +img, picture { + max-width: 100%; + display: block; +} + +input, button, textarea, select { + font: inherit; +} + +@media (prefers-reduced-motion: reduce) { + html:focus-within { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} \ No newline at end of file diff --git a/website/css/typography.css b/website/css/typography.css new file mode 100644 index 0000000..d014d67 --- /dev/null +++ b/website/css/typography.css @@ -0,0 +1,45 @@ +@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Syne:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap'); + +body { + font-family: 'Syne', sans-serif; + font-weight: 400; + line-height: 1.65; + letter-spacing: 0.01em; +} + +h1, h2, h3, h4, h5, h6, .serif { + font-family: 'Cormorant Garamond', serif; +} + +code, pre, .mono { + font-family: 'JetBrains Mono', monospace; +} + +/* Specific type treatments based on guidelines */ +.headline-hero { + font-size: var(--text-hero); + line-height: 0.92; + font-weight: 400; +} + +.headline-section { + font-size: var(--text-2xl); + font-weight: 400; +} + +.headline-card { + font-size: 1.2rem; + font-weight: 600; +} + +.ui-label { + font-family: 'Syne', sans-serif; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.stat-number { + font-family: 'Cormorant Garamond', serif; + font-weight: 600; +} \ No newline at end of file diff --git a/website/css/variables.css b/website/css/variables.css new file mode 100644 index 0000000..f5592b7 --- /dev/null +++ b/website/css/variables.css @@ -0,0 +1,59 @@ +:root { + /* ─── THE ONE ACCENT ───────────────────────────── */ + --pink: #F472B6; /* cat's nose — PRIMARY accent, used sparingly */ + --pink-light: #FBCFE8; /* inner ear blush — for tints, hover overlays */ + --pink-deep: #BE185D; /* deeper rose — pressed states, depth */ + --pink-glow: rgba(244, 114, 182, 0.2); + --pink-border: rgba(244, 114, 182, 0.3); + + /* ─── WARM DARK BASE ───────────────────────────── */ + /* Note: use warm near-black — NOT cold blue-black. + Add a tiny hint of red-brown to every bg tone. */ + --bg-void: #08070A; /* deepest — almost pure black */ + --bg-base: #0D0B0E; /* page background */ + --bg-layer: #141118; /* section alt bg */ + --bg-surface: #1B1820; /* cards, inputs */ + --bg-raised: #221F2A; /* hover, elevated */ + + /* ─── BORDERS ──────────────────────────────────── */ + --border: rgba(255, 255, 255, 0.07); + --border-hover: rgba(244, 114, 182, 0.35); + --border-pink: rgba(244, 114, 182, 0.2); + + /* ─── TEXT ─────────────────────────────────────── */ + --text-primary: #F0EAF5; /* warm ivory-white, not cold white */ + --text-secondary: #8E8098; /* muted warm grey-violet */ + --text-muted: #4D4557; /* near-invisible placeholder text */ + + /* ─── STRUCTURAL WHITES (from logo body) ──────── */ + --cat-white: #FFFFFF; + --ink: #1E293B; /* cat outline — only used for decorative logo elements */ + + /* ─── TYPOGRAPHY ───────────────────────────────── */ + --text-xs: clamp(0.7rem, 1vw, 0.8rem); + --text-sm: clamp(0.8rem, 1.2vw, 0.9rem); + --text-base: clamp(0.9rem, 1.5vw, 1.05rem); + --text-lg: clamp(1.1rem, 2vw, 1.35rem); + --text-xl: clamp(1.5rem, 3vw, 2.2rem); + --text-2xl: clamp(2.2rem, 5vw, 3.8rem); + --text-hero: clamp(3rem, 9vw, 7.5rem); + + /* ─── SPACING & SHAPE ──────────────────────────── */ + --radius-sm: 6px; + --radius-md: 12px; + --radius-lg: 20px; + --radius-xl: 36px; + --radius-pill: 9999px; + + --max-w: 1140px; + --section-pad: clamp(5rem, 9vw, 9rem) 1.5rem; + + /* ─── GLOW & SHADOW ────────────────────────────── */ + --glow-xs: 0 0 12px var(--pink-glow); + --glow-sm: 0 0 24px var(--pink-glow); + --glow-md: 0 0 48px var(--pink-glow), 0 0 96px rgba(244,114,182,0.08); + --glow-hero: 0 0 80px rgba(244,114,182,0.18), 0 0 160px rgba(244,114,182,0.06); + + --shadow-card: 0 2px 24px rgba(0,0,0,0.6), 0 1px 3px rgba(0,0,0,0.5); + --shadow-deep: 0 20px 60px rgba(0,0,0,0.7); +} \ No newline at end of file diff --git a/website/index.html b/website/index.html new file mode 100644 index 0000000..30750d9 --- /dev/null +++ b/website/index.html @@ -0,0 +1,1035 @@ + + + + + + PauseCat — The High-Performance Windows Break Reminder + + + + + + + + + + +
+
+
+ + + + + + + Windows 10 / 11 +
+ +

+ Your screen
can wait. +

+ +

+ PauseCat lives in your Windows system tray and enforces break + intervals — blurring your screen, counting down a glassmorphic + overlay, and resuming only when it's done.
+ Built in Rust. Under 1.5 MB. Always watching. +

+ + + + + +

+ "Increase productivity with paws-itive breaks" +

+ +
+
+ + + + + + + stars +
+ · +
+ + + + + + + + + downloads +
+ · +
+ + + + + + + + latest +
+
+ +

+ PauseCat_Installer.msi · + + · Apache 2.0 · Requires Windows 10/11 + WebView2 +

+
+
+
+ + +
+
+
+ TAKE A BREAK BUILT IN RUST + WINDOWS NATIVE + APACHE 2.0 + < 1.5 MB + OPEN SOURCE + +
+
+ TAKE A BREAK BUILT IN RUST + WINDOWS NATIVE + APACHE 2.0 + < 1.5 MB + OPEN SOURCE + +
+
+ TAKE A BREAK BUILT IN RUST + WINDOWS NATIVE + APACHE 2.0 + < 1.5 MB + OPEN SOURCE + +
+
+
+ + +
+
+
+ THE BREAK EXPERIENCE +
+

+ "When it's time, PauseCat takes over." +

+ +
+
+
+
PAUSE
+
+
+
04:58
+
TAKE A BREAK
+
You've worked 1h 0m
+ +
+
+
SOFT MODE
+
+
+ +
+
+ + + + Hardware Accelerated +
+
+ + + + Soft Mode — Skippable +
+
+ + + + Theme-Aware +
+
+
+
+ + +
+
+
+ HOW IT WORKS +
+

+ "Dead simple to set up." +

+ +
+
+
01
+

Install

+

+ "One MSI. That's it." +

+

+ Download PauseCat_Installer.msi from GitHub Releases. + The WiX installer checks for the WebView2 runtime and registers + autostart. Run it, find the cat in your tray. +

+
+
+ +
+
02
+

Configure

+

+ "Right-click. Settings. Done." +

+

+ Set work interval (5 min – 4 hours), break duration, break mode + (Soft or Hard), custom messages, and 3D text style. Every setting + live-previews inside the settings window. +

+
+
+ +
+
03
+

Work. Break. Repeat.

+

+ "PauseCat handles the rest." +

+

+ Interval fires → GDI captures your screen → Rust blurs it → + WebView2 break overlay appears. Lock your screen? Timer pauses. + Playing music? PauseCat pauses it. Sleep? Timer resumes when + you're back. +

+
+
+
+
+ + +
+
+

+ Everything you'd engineer yourself.
+ Already done. +

+ +
+
+
FULL CONFIG
+

+ "Deep Customization" +

+

+ Soft or Hard mode. Custom break messages, randomized. 3D text + behind the overlay: rotation X/Y/Z, float/rotate/swing/pulse + animation, opacity, depth, glow. Bubble: size, position, opacity. +

+ +
+
+
Work Interval
+
60 min
+
5 min – 4 hours
+
+
+
Break Duration
+
5 min
+
1 min – 2 hours
+
+
+
Mode
+
Soft
+
Soft / Hard
+
+
+
Autostart
+
On
+
On / Off
+
+
+
+ +
+
OVERLAY ENGINE
+

"GDI Blur Background"

+

+ Win32 GDI BitBlt screen capture + a pure Rust Gaussian blur. + Instantaneous, zero-flicker. The background you see during a break + is your actual screen, not a screenshot-of-a-screenshot. +

+
+ +
+
HARDWARE UI
+

"WebView2-Accelerated Overlay"

+

+ The break bubble runs inside a WebView2 host — the Chromium engine + powering Edge. Smooth animations, true + backdrop-filter, hardware-accelerated rendering. +

+
+ +
+
SESSION AWARE
+

"Intelligent Timer Pausing"

+

+ WTS session events: screen lock → timer pauses. Wake from sleep → + timer resumes. PBT power events for suspend/resume. It never + counts idle time as work time. +

+
+ +
+
MEDIA CONTROL
+

"SMTC Integration"

+

+ Windows System Media Transport Controls. Break starts → active + media pauses. Break ends → media resumes. Your Spotify keeps + playing context, not state. +

+
+ +
+
LIGHTWEIGHT
+

"< 1.5 MB Binary"

+

+ opt-level = 'z', fat LTO, single codegen unit, UPX + LZMA compression. The installer is smaller than most browser + extension icons. +

+
+
+
+
+ + +
+
+

+ "How hard should it push?" +

+ +
+
+
SOFT MODE
+

+ "Gentle nudge" +

+

+ The overlay appears with a countdown and a Skip button. You're an + adult — PauseCat reminds, never locks you out. +

+

+ Default mode. Good for most users. +

+
+ +
+
+ HARD MODE +
+

+ "Full enforcement" +

+

+ No Skip button. No dismiss. The countdown runs to zero. You set + the interval — PauseCat holds you to it. +

+

+ For users who know they'll skip soft breaks every time. +

+
+ +
+
SETTINGS.JSON
+
+{
+  "behavior": {
+    "mode": "Hard",
+    "interval": 3600,
+    "break": 300
+  },
+  "overlay": {
+    "blur_radius": 45,
+    "theme": "Dark"
+  }
+}
+

+ Easily configured via the tray UI. +

+
+
+
+
+ + +
+
+
+
+ APACHE 2.0 +

+ "Fully open. Read every line." +

+

+ Clean Rust architecture: state machine, GDI capture, Gaussian + blur, WebView2 host, WinHTTP update engine — all on GitHub. Fork + it, audit it, improve it. +

+ +
+
+
+
GitHub Stars
+
+
+
+
Total Downloads
+
+
+
+
Latest Version
+
+
+ + + View Source on GitHub + +
+ +
+
+
+
+
+
+
+
pausecat — PowerShell
+
+
+ +
+
+
+
+
+ + +
+
+

+ "One file. One click. Running." +

+ +
+
+
+
1
+
+
+
+

+ Download from GitHub Releases +

+

+ [PauseCat_Installer.msi] +

+
+
+ +
+
+
2
+
+
+
+

Run PauseCat_Installer.msi

+

+ WiX handles: WebView2 runtime check, autostart registry entry, + file installation. +

+
+
+ +
+
+
3
+
+
+

+ Find the cat in your system tray +

+

+ Right-click → Settings to configure your intervals, mode, and + overlay style. +

+
+
+
+ +
+ Windows 10 or 11 (64-bit)
+ WebView2 Runtime (auto-installed by WiX)
+ ~1.5 MB disk space +
+ + +
+
+ + +
+
+

+ "Built by one. Improved by many." +

+ + + +
+
+# Prerequisites: Rust stable · WiX Toolset v4 · WebView2 Runtime
+
+git clone https://github.com/0xarchit/pauseCat
+cd pauseCat
+cargo run       # development
+cargo test      # test suite
+
+
+
+
+ + +
+
+ Logo +

+ "Your next break is overdue." +

+

+ Free. Open source. Already running on Windows machines that need it. +

+ + + + + + + + + Download for Windows + + +

+ · Apache 2.0 · Windows 10/11 · + < 1.5 MB +

+
+
+ + + + + diff --git a/website/js/github-stats.js b/website/js/github-stats.js new file mode 100644 index 0000000..96629f7 --- /dev/null +++ b/website/js/github-stats.js @@ -0,0 +1,98 @@ +const REPO = '0xarchit/pauseCat'; +const CACHE_KEY = 'pausecat_gh_stats'; +const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes + +const FALLBACKS = { + stars: '—', + downloads: '—', + version: '—', + msiUrl: 'https://github.com/0xarchit/pauseCat/releases/latest', +}; + +function formatNumber(n) { + if (typeof n !== 'number') return '—'; + if (n >= 1000) return (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k'; + return n.toLocaleString(); +} + +function applyStats(stats) { + document.querySelectorAll('[data-stat="stars"]') + .forEach(el => el.textContent = stats.stars); + document.querySelectorAll('[data-stat="downloads"]') + .forEach(el => el.textContent = stats.downloads); + document.querySelectorAll('[data-stat="version"]') + .forEach(el => el.textContent = stats.version); + + // Update all download links to point to the real MSI asset + if (stats.msiUrl) { + document.querySelectorAll('[data-action="download"]') + .forEach(el => el.href = stats.msiUrl); + } +} + +async function fetchStats() { + // ── 1. Check cache ───────────────────────────────────────────── + try { + const cached = JSON.parse(localStorage.getItem(CACHE_KEY) || 'null'); + if (cached && Date.now() - cached.ts < CACHE_TTL_MS) { + applyStats(cached.data); + return; + } + } catch (_) { /* corrupt cache — ignore, re-fetch */ } + + // ── 2. Apply fallbacks immediately so page never shows empty ─── + applyStats(FALLBACKS); + + // ── 3. Fetch in parallel ─────────────────────────────────────── + try { + const headers = { 'Accept': 'application/vnd.github+json' }; + + const [repoRes, latestRes, allReleasesRes] = await Promise.all([ + fetch(`https://api.github.com/repos/${REPO}`, { headers }), + fetch(`https://api.github.com/repos/${REPO}/releases/latest`, { headers }), + fetch(`https://api.github.com/repos/${REPO}/releases?per_page=100`, { headers }), + ]); + + if (!repoRes.ok || !latestRes.ok || !allReleasesRes.ok) { + throw new Error('One or more API requests failed'); + } + + const [repo, latest, allReleases] = await Promise.all([ + repoRes.json(), + latestRes.json(), + allReleasesRes.json(), + ]); + + // ── Stars ──────────────────────────────────────────────────── + const stars = formatNumber(repo.stargazers_count); + + // ── Version ────────────────────────────────────────────────── + const version = latest.tag_name || FALLBACKS.version; + + // ── Total downloads: sum every asset across every release ──── + const totalDownloads = Array.isArray(allReleases) + ? allReleases.reduce((total, release) => + total + (release.assets || []).reduce((sum, asset) => + sum + (asset.download_count || 0), 0), 0) + : 0; + const downloads = formatNumber(totalDownloads); + + // ── MSI download URL from latest release ──────────────────── + const msiAsset = (latest.assets || []).find(a => a.name.endsWith('.msi')); + const msiUrl = msiAsset?.browser_download_url || FALLBACKS.msiUrl; + + const stats = { stars, downloads, version, msiUrl }; + + // ── 4. Update DOM ───────────────────────────────────────────── + applyStats(stats); + + // ── 5. Cache result ────────────────────────────────────────── + localStorage.setItem(CACHE_KEY, JSON.stringify({ ts: Date.now(), data: stats })); + + } catch (err) { + // Fallbacks already applied in step 2 — nothing more to do + console.warn('[PauseCat] GitHub stats unavailable:', err.message); + } +} + +export { fetchStats }; \ No newline at end of file diff --git a/website/js/interactive.js b/website/js/interactive.js new file mode 100644 index 0000000..46d44cf --- /dev/null +++ b/website/js/interactive.js @@ -0,0 +1,20 @@ +export function initInteractive() { + if (!window.matchMedia('(pointer: fine)').matches) return; + + // Create ambient lighting overlay + const ambientGlow = document.createElement('div'); + ambientGlow.id = 'ambient-glow'; + document.body.appendChild(ambientGlow); + + window.addEventListener('mousemove', (e) => { + // Global background ambient light tracking + document.documentElement.style.setProperty('--mouse-x', `${e.clientX}px`); + document.documentElement.style.setProperty('--mouse-y', `${e.clientY}px`); + }); + + ambientGlow.style.opacity = '0'; + + window.addEventListener('mousemove', () => { + ambientGlow.style.opacity = '1'; + }, { once: true }); +} \ No newline at end of file diff --git a/website/js/main.js b/website/js/main.js new file mode 100644 index 0000000..28b36db --- /dev/null +++ b/website/js/main.js @@ -0,0 +1,13 @@ +import { fetchStats } from './github-stats.js'; +import { initScrollReveal } from './scroll-reveal.js'; +import { initNav } from './nav.js'; +import { initTypewriter } from './typewriter.js'; +import { initInteractive } from './interactive.js'; + +document.addEventListener('DOMContentLoaded', () => { + fetchStats(); + initScrollReveal(); + initNav(); + initTypewriter(); + initInteractive(); +}); \ No newline at end of file diff --git a/website/js/nav.js b/website/js/nav.js new file mode 100644 index 0000000..bad2ebf --- /dev/null +++ b/website/js/nav.js @@ -0,0 +1,14 @@ +export function initNav() { + const nav = document.querySelector('nav'); + + window.addEventListener('scroll', () => { + if (window.scrollY > 60) { + nav.classList.add('scrolled'); + } else { + nav.classList.remove('scrolled'); + } + }); + + // Add mobile menu logic here if needed later. + // Currently sticking to the core desktop spec but ready for extension. +} \ No newline at end of file diff --git a/website/js/scroll-reveal.js b/website/js/scroll-reveal.js new file mode 100644 index 0000000..b7c69a8 --- /dev/null +++ b/website/js/scroll-reveal.js @@ -0,0 +1,26 @@ +export function initScrollReveal() { + const observer = new IntersectionObserver(entries => { + entries.forEach((entry, i) => { + if (entry.isIntersecting) { + // slight stagger if multiple elements enter at once + entry.target.style.transitionDelay = `${i * 90}ms`; + entry.target.classList.add('revealed'); + observer.unobserve(entry.target); + } + }); + }, { threshold: 0.12 }); + + document.querySelectorAll('.reveal').forEach(el => observer.observe(el)); + + // Also handle the hero scroll cue visibility + const cue = document.querySelector('.scroll-cue'); + if (cue) { + const handleScroll = () => { + if (window.scrollY > 60) { + cue.classList.add('hidden'); + window.removeEventListener('scroll', handleScroll); + } + }; + window.addEventListener('scroll', handleScroll); + } +} \ No newline at end of file diff --git a/website/js/typewriter.js b/website/js/typewriter.js new file mode 100644 index 0000000..fa36a25 --- /dev/null +++ b/website/js/typewriter.js @@ -0,0 +1,91 @@ +export function initTypewriter() { + const terminalBody = document.querySelector('.terminal-body'); + if (!terminalBody) return; + + const script = [ + { type: 'prompt', text: 'git clone https://github.com/0xarchit/pauseCat' }, + { type: 'output', text: 'Cloning into \'pauseCat\'...' }, + { type: 'prompt', text: 'cd pauseCat' }, + { type: 'output', text: 'Read the code. Audit the architecture.' }, + { type: 'output', text: 'To run, download the MSI from Releases.' } + ]; + + let lineIndex = 0; + let charIndex = 0; + let currentLineEl = null; + let isTyping = false; + + // Clear existing static content if any, except for setting up structure + terminalBody.innerHTML = ''; + + const createLine = (type) => { + const div = document.createElement('div'); + if (type === 'prompt') { + const ps = document.createElement('span'); + ps.className = 'term-prompt'; + ps.textContent = 'PS C:\\> '; + div.appendChild(ps); + } + return div; + }; + + const cursor = document.createElement('span'); + cursor.className = 'term-cursor'; + + const typeNextChar = () => { + if (lineIndex >= script.length) { + terminalBody.appendChild(cursor); + return; + } + + const currentLine = script[lineIndex]; + + if (charIndex === 0) { + if (currentLineEl && currentLineEl.contains(cursor)) { + currentLineEl.removeChild(cursor); + } + currentLineEl = createLine(currentLine.type); + terminalBody.appendChild(currentLineEl); + if (currentLine.type !== 'output' && currentLine.type !== 'success') { + currentLineEl.appendChild(cursor); + } + } + + if (currentLine.type === 'prompt') { + // Type out prompt character by character + if (charIndex < currentLine.text.length) { + const textNode = document.createTextNode(currentLine.text.charAt(charIndex)); + currentLineEl.insertBefore(textNode, cursor); + charIndex++; + setTimeout(typeNextChar, 40); + } else { + // Line finished + charIndex = 0; + lineIndex++; + setTimeout(typeNextChar, 200); + } + } else { + // Instantly show output lines + const span = document.createElement('span'); + if (currentLine.type === 'success') { + span.className = 'term-success'; + } + span.textContent = currentLine.text; + currentLineEl.appendChild(span); + + charIndex = 0; + lineIndex++; + setTimeout(typeNextChar, 300); + } + }; + + const observer = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting && !isTyping) { + isTyping = true; + setTimeout(typeNextChar, 500); + observer.disconnect(); + } + }, { threshold: 0.5 }); + + observer.observe(terminalBody); +} \ No newline at end of file