From c3f02d688668358d97b4d78cc979045df4548f9e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 21:46:08 +0000 Subject: [PATCH 1/4] =?UTF-8?q?Redesign=20portfolio=20with=20Japanese=20mi?= =?UTF-8?q?nimalism=20=C3=97=20Swiss=20typography?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete visual overhaul removing glassmorphism in favour of a precise, typographic design system. - Design tokens: warm off-white / near-black palette, single accent (#1246F0), Syne display + DM Sans body + JetBrains Mono labels - Custom cursor: 10px dot expands to ring on hover; dragging state; mix-blend drives visibility on any background - Grain texture: fixed 3.2% opacity SVG noise overlay for depth - Progress bar: 1.5px accent line tracks horizontal section position - Desktop: full-bleed sections, ghost section numbers (4% opacity), animated hero (name → rule → footer), text-only nav with animated top-edge indicator line - Mobile: matching typographic treatment, minimal dot navigation, clean slide-out menu with mono numbering - Motion: cubic-bezier(0.16, 1, 0.3) easing throughout; staggered entrance animations on home section; underline extends on link hover https://claude.ai/code/session_01BkB715SdTfJR99BjBzB1N4 --- css/base.css | 378 +++++++++++++++----------------- css/desktop.css | 459 +++++++++++++++++---------------------- css/mobile-portrait.css | 469 +++++++++++++++------------------------- index.html | 375 +++++++++++++++----------------- js/main.js | 61 +++++- 5 files changed, 780 insertions(+), 962 deletions(-) diff --git a/css/base.css b/css/base.css index 1ff230e..54f1cbd 100644 --- a/css/base.css +++ b/css/base.css @@ -1,252 +1,218 @@ +/* ========================================================= + BASE — Brad Cooley Portfolio + Design System: Japanese Minimalism × Swiss Typography + ========================================================= */ + +/* ── Custom Properties: Light Mode ─────────────────────── */ :root { - /* Color system */ - --text-color: #1e293b; - --text-color-dark: #f1f5f9; - --text-color-secondary: #64748b; - --background-color: #e2e8f0; - --background-color-dark: #0f172a; - - /* Glass effect variables */ - --glass-primary: rgba(255, 255, 255, 0.4); - --glass-primary-dark: rgba(15, 23, 42, 0.4); - --glass-secondary: rgba(255, 255, 255, 0.2); - --glass-secondary-dark: rgba(15, 23, 42, 0.2); - --glass-border: rgba(255, 255, 255, 0.5); - --glass-border-dark: rgba(255, 255, 255, 0.15); - --glass-shadow: 0 8px 32px rgba(31, 38, 135, 0.25); - --glass-shadow-dark: 0 8px 32px rgba(0, 0, 0, 0.4); - - /* Gradients */ - --accent-gradient: linear-gradient( - 135deg, - #3b82f6 0%, - #8b5cf6 50%, - #ec4899 100% - ); - --accent-gradient-dark: linear-gradient( - 135deg, - #4f46e5 0%, - #7c3aed 50%, - #ec4899 100% - ); - --gradient-colors: linear-gradient( - 45deg, - #6366f1 0%, - #8b5cf6 33%, - #ec4899 66%, - #f59e0b 100% - ); + /* Color */ + --bg: #F8F7F4; + --bg-raised: #FFFFFF; + --ink: #141412; + --ink-2: #6B6A67; + --ink-3: #B4B2AE; + --line: #E5E3DF; + --accent: #1246F0; + --accent-sub: rgba(18, 70, 240, 0.06); /* Typography */ - --font-heading: "Poppins", -apple-system, BlinkMacSystemFont, "SF Pro Display", - "Helvetica Neue", sans-serif; - --font-body: "Lato", -apple-system, BlinkMacSystemFont, "SF Pro Text", - "Helvetica Neue", sans-serif; - - /* Transitions and timing */ - --transition-fast: 0.2s ease; - --transition-smooth: 0.3s ease; - --transition-slow: 0.6s cubic-bezier(0.25, 0.1, 0.25, 1); - --animation-duration: 8s; -} - -/* Modern CSS reset */ -*, -*::before, -*::after { + --font-display: 'Syne', sans-serif; + --font-body: 'DM Sans', sans-serif; + --font-mono: 'JetBrains Mono', monospace; + + /* Type scale */ + --text-xs: 0.6875rem; + --text-sm: 0.8125rem; + --text-base: 1rem; + --text-lg: 1.125rem; + --text-xl: 1.375rem; + --text-2xl: 1.75rem; + --text-3xl: 2.25rem; + --text-4xl: 3rem; + --text-5xl: 4rem; + --text-hero: clamp(4.5rem, 10vw, 10rem); + + /* Spacing (4px base) */ + --sp-1: 4px; + --sp-2: 8px; + --sp-3: 12px; + --sp-4: 16px; + --sp-5: 20px; + --sp-6: 24px; + --sp-8: 32px; + --sp-10: 40px; + --sp-12: 48px; + --sp-16: 64px; + --sp-20: 80px; + --sp-24: 96px; + + /* Motion */ + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); + --t-fast: 120ms; + --t-base: 280ms; + --t-slow: 500ms; + --t-enter: 700ms; + --t-section: 600ms; +} + +/* ── Dark Mode ──────────────────────────────────────────── */ +@media (prefers-color-scheme: dark) { + :root { + --bg: #0F0F0E; + --bg-raised: #1A1A18; + --ink: #F0EFE9; + --ink-2: #8A8883; + --ink-3: #555350; + --line: #2D2C2A; + --accent: #4F7BF7; + --accent-sub: rgba(79, 123, 247, 0.08); + } +} + +/* ── Reset ──────────────────────────────────────────────── */ +*, *::before, *::after { + box-sizing: border-box; margin: 0; padding: 0; - box-sizing: border-box; } html { - height: 100%; font-size: 16px; + -webkit-text-size-adjust: 100%; + text-size-adjust: 100%; } body { - height: 100%; - overflow: hidden; font-family: var(--font-body); + background: var(--bg); + color: var(--ink); line-height: 1.6; - color: var(--text-color); - background: var(--background-color); - position: relative; - transition: var(--transition-smooth); + height: 100%; + overflow: hidden; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; } -/* Background system - Static gradient base */ -body::before { - content: ""; - position: fixed; - inset: 0; - background: var(--accent-gradient); - z-index: -2; +/* Hide default cursor on fine-pointer (mouse) devices */ +@media (pointer: fine) { + * { cursor: none !important; } } -/* Background overlay for glass effect */ -body::after { - content: ""; +/* ── Custom Cursor ──────────────────────────────────────── */ +.cursor { + display: none; + position: fixed; + top: 0; + left: 0; + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--ink); + pointer-events: none; + z-index: 99999; + will-change: transform; + transition: + width var(--t-base) var(--ease-out), + height var(--t-base) var(--ease-out), + background var(--t-base) var(--ease-out), + box-shadow var(--t-base) var(--ease-out), + opacity var(--t-fast); +} + +@media (pointer: fine) { + .cursor { display: block; } +} + +.cursor.is-hovering { + width: 36px; + height: 36px; + background: transparent; + box-shadow: inset 0 0 0 1.5px var(--ink); +} + +.cursor.is-dragging { + width: 52px; + height: 52px; + background: transparent; + box-shadow: inset 0 0 0 1px var(--ink); + opacity: 0.45; +} + +/* ── Grain Texture Overlay ──────────────────────────────── */ +.grain { position: fixed; inset: 0; - background: var(--background-color); - opacity: 0.75; - z-index: -1; + pointer-events: none; + z-index: 9998; + opacity: 0.032; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); + background-size: 180px; + background-repeat: repeat; } -/* Core animations */ -@keyframes textGradient { - 0%, - 100% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } +/* ── Progress Bar ───────────────────────────────────────── */ +.progress-bar { + position: fixed; + top: 0; + left: 0; + height: 1.5px; + background: var(--accent); + z-index: 10000; + width: 0%; + transition: width var(--t-section) var(--ease-out); + pointer-events: none; } -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(40px); - } - to { - opacity: 1; - transform: translateY(0); - } +/* ── Keyframes ──────────────────────────────────────────── */ +@keyframes fadeUp { + from { opacity: 0; transform: translateY(18px); } + to { opacity: 1; transform: translateY(0); } } -@keyframes gradientShift { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } } -/* Utility classes for common patterns */ -.gradient-text { - background: var(--gradient-colors); - background-size: 200% 200%; - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - animation: textGradient var(--animation-duration) ease infinite; +@keyframes lineExpand { + from { transform: scaleX(0); } + to { transform: scaleX(1); } } -.glass-effect { - background: var(--glass-primary); - backdrop-filter: blur(25px); - -webkit-backdrop-filter: blur(25px); - border: 1px solid var(--glass-border); - box-shadow: var(--glass-shadow); +/* ── Utility ────────────────────────────────────────────── */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; } -.glass-effect-secondary { - background: var(--glass-secondary); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); - border: 1px solid var(--glass-border); +:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 3px; + border-radius: 2px; } -/* Dark mode support */ -@media (prefers-color-scheme: dark) { - :root { - --text-color: var(--text-color-dark); - --background-color: var(--background-color-dark); - --glass-primary: var(--glass-primary-dark); - --glass-secondary: var(--glass-secondary-dark); - --glass-border: var(--glass-border-dark); - --glass-shadow: var(--glass-shadow-dark); - --accent-gradient: var(--accent-gradient-dark); - } - - body::after { - background: var(--background-color-dark); - opacity: 0.85; - } -} - -/* Accessibility improvements */ +/* ── Reduced Motion ─────────────────────────────────────── */ @media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { + *, *::before, *::after { animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; - scroll-behavior: auto !important; - } - - .scroll-hint { - display: none; } + .cursor { display: none !important; } } -/* High contrast mode support */ -@media (prefers-contrast: high) { - :root { - --glass-border: rgba(255, 255, 255, 0.8); - --glass-border-dark: rgba(255, 255, 255, 0.6); - } - - .nav-item:focus, - .social-link:focus, - .scroll-menu-item:focus { - outline: 3px solid currentColor; - outline-offset: 2px; - } -} - -/* Print styles */ +/* ── Print ──────────────────────────────────────────────── */ @media print { - body::before, - body::after { - display: none; - } - - .bottom-nav, - .scroll-indicator, - .scroll-menu, - .scroll-hint { - display: none; - } - - .section { - page-break-inside: avoid; - } -} - -/* Focus management for keyboard users */ -.js-focus-visible :focus:not(.focus-visible) { - outline: none; -} - -/* Smooth scrolling for supported browsers */ -@supports (scroll-behavior: smooth) { - html { - scroll-behavior: smooth; - } -} - -/* Modern backdrop-filter fallback */ -@supports not (backdrop-filter: blur()) { - .glass-effect, - .glass-effect-secondary { - background: rgba(255, 255, 255, 0.9); - } - - @media (prefers-color-scheme: dark) { - .glass-effect, - .glass-effect-secondary { - background: rgba(15, 23, 42, 0.9); - } + .cursor, .grain, .progress-bar, + .bottom-nav, .scroll-indicator, .scroll-menu, .scroll-hint { + display: none !important; } } diff --git a/css/desktop.css b/css/desktop.css index c700940..e4c6048 100644 --- a/css/desktop.css +++ b/css/desktop.css @@ -1,371 +1,310 @@ +/* ========================================================= + DESKTOP — Brad Cooley Portfolio + ≥ 768px · Horizontal Scroll Navigation + ========================================================= */ + @media (min-width: 768px) { - .mobile-portrait-layout, - .mobile-landscape-layout { + + /* ── Layout ─────────────────────────────────────────── */ + .mobile-portrait-layout { display: none; } .desktop-layout { display: block; + width: 100vw; + height: 100vh; + overflow: hidden; } - /* Main container and sections */ .app-container { height: 100vh; width: 100vw; - position: relative; overflow: hidden; } + /* ── Sections Container ─────────────────────────────── */ .sections-container { - height: 100vh; - width: 500vw; display: flex; - transition: var(--transition-slow); - will-change: transform; + width: 500vw; + height: calc(100vh - 56px); transform: translateX(0); - cursor: grab; + transition: transform var(--t-section) var(--ease-out); + will-change: transform; user-select: none; } - .sections-container:active { - cursor: grabbing; - } - .sections-container.no-transition { transition: none; } - .sections-container.at-start { - cursor: e-resize; - } - - .sections-container.at-end { - cursor: w-resize; - } - + /* ── Individual Section ─────────────────────────────── */ .section { width: 100vw; - height: 100vh; + height: 100%; display: flex; align-items: center; - justify-content: center; - text-align: center; - padding: 2rem; - position: relative; + justify-content: flex-start; flex-shrink: 0; - pointer-events: auto; - transform: translateY(-2rem); - } - - /* Glass containers */ - .glass-container { - background: var(--glass-primary); - backdrop-filter: blur(25px); - -webkit-backdrop-filter: blur(25px); - border-radius: 32px; - border: 1px solid var(--glass-border); - box-shadow: var(--glass-shadow); position: relative; overflow: hidden; - transition: var(--transition-smooth); + padding: var(--sp-10) clamp(var(--sp-10), 8vw, var(--sp-24)); } - .glass-container::before { - content: ""; + /* ── Ghost Section Number ───────────────────────────── */ + .section-ghost-num { position: absolute; - inset: 0; - border-radius: 32px; - padding: 1px; - background: var(--accent-gradient); - mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - mask-composite: subtract; - -webkit-mask-composite: subtract; - opacity: 0.6; + bottom: var(--sp-6); + right: clamp(var(--sp-10), 6vw, var(--sp-20)); + font-family: var(--font-display); + font-size: clamp(7rem, 14vw, 16rem); + font-weight: 800; + color: var(--ink); + opacity: 0.038; + line-height: 1; + user-select: none; pointer-events: none; + letter-spacing: -0.04em; } - .home-glass, - .section-glass { - padding: 4rem 3rem; - max-width: 700px; - width: 90%; - margin: 0 auto; - min-height: 500px; + /* ── Home Section ───────────────────────────────────── */ + .home-content { + width: min(960px, 90%); display: flex; flex-direction: column; - justify-content: center; - align-items: center; + gap: var(--sp-6); } - /* Home section specific styles */ - .home-content { - opacity: 0; - animation: fadeInUp 1.2s ease-out forwards; - text-align: center; - width: 100%; - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - height: 100%; - min-height: 400px; + .home-name h1 { + font-family: var(--font-display); + font-size: var(--text-hero); + font-weight: 800; + line-height: 0.91; + letter-spacing: -0.04em; + color: var(--ink); + animation: fadeUp var(--t-enter) var(--ease-out) 0.1s both; } - .name-display { - margin-bottom: 2rem; + .home-rule { + width: 100%; + height: 1px; + background: var(--line); + transform-origin: left; + animation: lineExpand var(--t-slow) var(--ease-out) 0.35s both; } - .name-display h1 { - font-family: var(--font-heading); - font-size: clamp(4rem, 12vw, 8rem); - font-weight: 700; - letter-spacing: -0.03em; - text-rendering: optimizeLegibility; - background: var(--gradient-colors); - background-size: 200% 200%; - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - animation: textGradient 8s ease infinite, fadeInUp 1.2s ease-out forwards; - margin-bottom: 0.5rem; - line-height: 0.9; - } - - .tagline { - font-size: clamp(1.2rem, 3vw, 1.6rem); - color: var(--text-color); - opacity: 0.8; - font-weight: 300; - margin-bottom: 2.5rem; - animation: fadeInUp 1.2s ease-out 0.3s both; + .home-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--sp-8); + animation: fadeUp var(--t-enter) var(--ease-out) 0.5s both; } - .social-section { - animation: fadeInUp 1.2s ease-out 0.6s both; + .home-title { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-2); + text-transform: uppercase; + letter-spacing: 0.14em; + line-height: 1; } - /* Social links */ - .social-links { - display: flex; - gap: 1.5rem; - justify-content: center; - flex-wrap: wrap; - } - - .social-link { - background: var(--glass-secondary); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); - border: 1px solid var(--glass-border); - border-radius: 20px; - padding: 1rem; - width: 60px; - height: 60px; + .home-links { display: flex; - align-items: center; - justify-content: center; + gap: var(--sp-6); + } + + .home-links a { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-2); text-decoration: none; - color: var(--text-color); - font-size: 1.5rem; - transition: var(--transition-smooth); + text-transform: uppercase; + letter-spacing: 0.1em; position: relative; - overflow: hidden; + padding-bottom: 1px; + transition: color var(--t-base) var(--ease-out); } - .social-link::before { - content: ""; + .home-links a::after { + content: ''; position: absolute; - inset: 0; - background: var(--accent-gradient); - opacity: 0; - transition: var(--transition-smooth); + bottom: 0; + left: 0; + width: 100%; + height: 1px; + background: var(--accent); + transform: scaleX(0); + transform-origin: left; + transition: transform var(--t-slow) var(--ease-out); } - .social-link:hover::before { - opacity: 0.1; + .home-links a:hover { + color: var(--ink); } - .social-link:hover { - transform: translateY(-3px) scale(1.05); - border-color: rgba(255, 255, 255, 0.3); - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); + .home-links a:hover::after { + transform: scaleX(1); } - .social-link i { - position: relative; - z-index: 1; + .link-arrow { + display: inline-block; + margin-left: 2px; + transition: transform var(--t-base) var(--ease-spring); } - /* Section content */ - .section-content { - opacity: 1; - transform: translateY(0); - display: flex; - flex-direction: column; - justify-content: space-between; + .home-links a:hover .link-arrow { + transform: translate(2px, -2px); + } + + /* ── Section Inner (About / Projects / Writing / Resume) */ + .section-inner { + width: min(960px, 90%); + display: grid; + grid-template-columns: 1fr 1.4fr; + gap: clamp(var(--sp-12), 8vw, var(--sp-20)); align-items: center; - height: 100%; - min-height: 400px; } - .section-icon { - font-size: 5rem; - color: var(--text-color); - margin-bottom: 2rem; - opacity: 0.8; + .section-label-col { + display: flex; + flex-direction: column; + gap: var(--sp-5); } - .section-title { - font-family: var(--font-heading); - font-size: clamp(2.5rem, 6vw, 3.5rem); - font-weight: 600; - margin-bottom: 1.5rem; - background: var(--gradient-colors); - background-size: 200% 200%; - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - animation: textGradient 8s ease infinite; - } - - .section-description { - font-size: clamp(1.1rem, 2.5vw, 1.3rem); - color: var(--text-color); - margin-bottom: 2.5rem; - line-height: 1.6; - opacity: 0.9; - } - - /* Coming soon badge */ - .coming-soon-badge { - display: inline-flex; - align-items: center; - gap: 0.8rem; - padding: 1rem 2rem; - background: var(--glass-secondary); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); - border-radius: 50px; - color: var(--text-color); - font-weight: 500; - border: 1px solid var(--glass-border); - transition: var(--transition-smooth); - position: relative; - overflow: hidden; + .section-eyebrow { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-3); + text-transform: uppercase; + letter-spacing: 0.14em; } - .coming-soon-badge::before { - content: ""; - position: absolute; - inset: 0; - background: var(--accent-gradient); - opacity: 0; - transition: var(--transition-smooth); + .section-title { + font-family: var(--font-display); + font-size: clamp(var(--text-3xl), 4.8vw, var(--text-5xl)); + font-weight: 800; + line-height: 0.95; + letter-spacing: -0.03em; + color: var(--ink); } - .coming-soon-badge:hover::before { - opacity: 0.05; + .section-text-col { + display: flex; + flex-direction: column; + gap: var(--sp-6); + padding-top: var(--sp-2); } - .coming-soon-badge:hover { - transform: translateY(-2px); - border-color: rgba(255, 255, 255, 0.3); + .section-body-text { + font-family: var(--font-body); + font-size: clamp(var(--text-lg), 1.4vw, var(--text-xl)); + font-weight: 300; + color: var(--ink-2); + line-height: 1.75; + max-width: 480px; } - .coming-soon-badge i { - position: relative; - z-index: 1; + .section-wip { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-3); + letter-spacing: 0.06em; } - /* Bottom navigation */ + /* ── Navigation Bar ─────────────────────────────────── */ .bottom-nav { position: fixed; - bottom: 2rem; - left: 50%; - transform: translateX(-50%); + bottom: 0; + left: 0; + right: 0; + height: 56px; z-index: 1000; - background: var(--glass-primary); - backdrop-filter: blur(25px); - -webkit-backdrop-filter: blur(25px); - border-radius: 25px; - border: 1px solid var(--glass-border); - box-shadow: var(--glass-shadow); - padding: 0.8rem 1.5rem; - transition: var(--transition-smooth); + background: var(--bg); + border-top: 1px solid var(--line); + display: flex; + align-items: stretch; } .nav-container { display: flex; - gap: 0.5rem; - align-items: center; + align-items: stretch; + padding: 0 clamp(var(--sp-8), 5vw, var(--sp-16)); + flex: 1; } .nav-item { display: flex; - flex-direction: column; align-items: center; - gap: 0.3rem; - padding: 0.8rem 1rem; - border-radius: 18px; - cursor: pointer; - transition: var(--transition-smooth); + gap: var(--sp-2); + padding: 0 var(--sp-5); + height: 100%; position: relative; - overflow: hidden; - min-width: 60px; + color: var(--ink-3); + transition: color var(--t-base) var(--ease-out); + flex-shrink: 0; + background: none; + border: none; + cursor: none; } + /* Animated top-edge indicator line */ .nav-item::before { - content: ""; + content: ''; position: absolute; - inset: 0; - background: var(--accent-gradient); - opacity: 0; - transition: var(--transition-smooth); + top: -1px; + left: 0; + right: 0; + height: 2px; + background: var(--ink); + transform: scaleX(0); + transform-origin: left; + transition: transform var(--t-slow) var(--ease-out); } - .nav-item.active::before, - .nav-item:hover::before { - opacity: 0.1; + .nav-item.active { + color: var(--ink); } - .nav-item.active { - background: var(--glass-secondary); + .nav-item.active::before { + transform: scaleX(1); } - .nav-icon { - font-size: 1.2rem; - color: var(--text-color); - position: relative; - z-index: 1; - transition: var(--transition-smooth); + .nav-item:hover:not(.active) { + color: var(--ink-2); } - .nav-item.active .nav-icon { - background: var(--gradient-colors); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-size: 200% 200%; - animation: textGradient 8s ease infinite; + .nav-item:hover:not(.active)::before { + transform: scaleX(0.3); } - .nav-label { - font-size: 0.7rem; - color: var(--text-color); - opacity: 0.8; - font-weight: 500; - position: relative; - z-index: 1; + .nav-num { + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.04em; + opacity: 0.5; + line-height: 1; } - .nav-item.active .nav-label { - opacity: 1; - font-weight: 600; + .nav-label { + font-family: var(--font-body); + font-size: var(--text-sm); + font-weight: 400; + letter-spacing: 0.01em; + line-height: 1; } - /* Focus states for accessibility */ - .nav-item:focus { - outline: 2px solid var(--glass-border); - outline-offset: 2px; + /* Subtle right-side year stamp */ + .nav-year { + margin-left: auto; + display: flex; + align-items: center; + padding-right: clamp(var(--sp-8), 5vw, var(--sp-16)); + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-3); + letter-spacing: 0.08em; + user-select: none; } } diff --git a/css/mobile-portrait.css b/css/mobile-portrait.css index cc91f2b..fb38bef 100644 --- a/css/mobile-portrait.css +++ b/css/mobile-portrait.css @@ -1,6 +1,12 @@ +/* ========================================================= + MOBILE PORTRAIT — Brad Cooley Portfolio + ≤ 767px · Portrait · Vertical Scroll Snap + ========================================================= */ + @media (max-width: 767px) and (orientation: portrait) { - .desktop-layout, - .mobile-landscape-layout { + + /* ── Layout ─────────────────────────────────────────── */ + .desktop-layout { display: none; } @@ -13,10 +19,8 @@ scroll-snap-type: y mandatory; scroll-behavior: smooth; -webkit-overflow-scrolling: touch; - /* Hide scrollbar */ -ms-overflow-style: none; scrollbar-width: none; - /* Improve scroll performance */ will-change: scroll-position; } @@ -24,231 +28,165 @@ display: none; } - /* Mobile sections */ + /* ── Mobile Sections ────────────────────────────────── */ .mobile-section { height: 100vh; width: 100vw; display: flex; align-items: center; - justify-content: center; - padding: 2rem 0; - padding-left: 2rem; - padding-right: 2.75rem; + justify-content: flex-start; + padding: var(--sp-12) var(--sp-8); + padding-right: calc(var(--sp-8) + 24px); scroll-snap-align: start; scroll-snap-stop: always; position: relative; } - /* Mobile cards */ - .mobile-card { - width: 100%; - height: 75%; - background: var(--glass-primary); - backdrop-filter: blur(25px); - border-radius: 24px; - border: 1px solid var(--glass-border); - box-shadow: var(--glass-shadow); + /* ── Mobile Content Block ───────────────────────────── */ + .mobile-content { display: flex; flex-direction: column; - align-items: center; - justify-content: space-between; - padding: 2rem 1.5rem; - text-align: center; - position: relative; - overflow: hidden; - animation: mobileCardIn 0.8s ease-out forwards; + gap: var(--sp-5); + width: 100%; + max-width: 340px; } - .mobile-card::before { - content: ""; - position: absolute; - inset: 0; - border-radius: 24px; - padding: 1px; - background: var(--accent-gradient); - mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - mask-composite: subtract; - -webkit-mask-composite: subtract; - opacity: 0.6; - pointer-events: none; + .mobile-eyebrow { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-3); + text-transform: uppercase; + letter-spacing: 0.14em; } - .mobile-section:last-child { - scroll-snap-align: end; + /* Home: large name */ + .mobile-name { + font-family: var(--font-display); + font-size: clamp(3.25rem, 13vw, 4.5rem); + font-weight: 800; + line-height: 0.91; + letter-spacing: -0.04em; + color: var(--ink); } - /* Card content */ - .mobile-icon { - font-size: 3.5rem; - margin-bottom: 1.5rem; - background: var(--gradient-colors); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-size: 200% 200%; - animation: textGradient 8s ease infinite; - flex-shrink: 0; + .mobile-rule { + width: 100%; + height: 1px; + background: var(--line); } - .mobile-title { - font-family: var(--font-heading); - font-size: 2.2rem; - font-weight: 600; - margin-bottom: 1rem; - background: var(--gradient-colors); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-size: 200% 200%; - animation: textGradient 8s ease infinite; - flex-shrink: 0; + .mobile-role { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-2); + text-transform: uppercase; + letter-spacing: 0.14em; + line-height: 1; } - .mobile-description { - font-size: 1rem; - color: var(--text-color); - opacity: 0.9; - line-height: 1.5; - margin-bottom: 2rem; - flex-grow: 1; + .mobile-links { display: flex; - align-items: center; - text-align: center; - } - - .mobile-badge { - display: inline-flex; - align-items: center; - gap: 0.5rem; - padding: 0.8rem 1.5rem; - background: var(--glass-secondary); - backdrop-filter: blur(15px); - border-radius: 50px; - color: var(--text-color); - font-weight: 500; - border: 1px solid var(--glass-border); - font-size: 0.9rem; - flex-shrink: 0; - transition: var(--transition-smooth); + gap: var(--sp-5); + margin-top: var(--sp-1); } - .mobile-badge:active { - transform: scale(0.95); + .mobile-links a { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-2); + text-decoration: none; + text-transform: uppercase; + letter-spacing: 0.1em; + -webkit-tap-highlight-color: transparent; + transition: color var(--t-fast); } - /* Home section specific styles */ - .mobile-section.home .mobile-title { - font-size: 2.8rem; - margin-bottom: 0.5rem; + .mobile-links a:active { + color: var(--ink); } - .mobile-section.home .mobile-description { - font-size: 1.1rem; - margin-bottom: 2rem; + .link-arrow { + display: inline-block; + margin-left: 2px; } - /* Social links in mobile */ - .mobile-section.home .social-links { - display: flex; - gap: 1rem; - justify-content: center; - flex-shrink: 0; + /* Non-home sections */ + .mobile-section-title { + font-family: var(--font-display); + font-size: clamp(2.25rem, 10vw, 3rem); + font-weight: 800; + line-height: 0.95; + letter-spacing: -0.03em; + color: var(--ink); } - .mobile-section.home .social-link { - background: var(--glass-secondary); - backdrop-filter: blur(15px); - border: 1px solid var(--glass-border); - border-radius: 16px; - padding: 0.8rem; - width: 45px; - height: 45px; - display: flex; - align-items: center; - justify-content: center; - text-decoration: none; - color: var(--text-color); - font-size: 1.2rem; - transition: var(--transition-fast); + .mobile-body-text { + font-family: var(--font-body); + font-size: var(--text-base); + font-weight: 300; + color: var(--ink-2); + line-height: 1.72; } - .mobile-section.home .social-link:active { - transform: scale(0.9); - background: var(--glass-border); + .mobile-wip { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-3); + letter-spacing: 0.06em; } - /* Scroll indicator */ + /* ── Scroll Indicator Dots ──────────────────────────── */ .scroll-indicator { position: fixed; - right: 0; + right: var(--sp-3); top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; - gap: 0.75rem; + gap: var(--sp-2); z-index: 1000; + padding: var(--sp-3); cursor: pointer; - padding: 1rem; - border-radius: 16px; - transition: var(--transition-smooth); - } - - .scroll-indicator:hover { - background: var(--glass-secondary); - backdrop-filter: blur(15px); + -webkit-tap-highlight-color: transparent; } .scroll-dot { - width: 10px; - height: 10px; - border-radius: 50%; - background: var(--glass-border); - transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1); - opacity: 0.4; + width: 4px; + height: 4px; + border-radius: 99px; + background: var(--ink-3); + transition: + height 220ms var(--ease-out), + background 220ms var(--ease-out), + opacity 220ms; + opacity: 0.45; pointer-events: none; - position: relative; - overflow: hidden; - } - - .scroll-dot::before { - content: ""; - position: absolute; - inset: 0; - background: var(--accent-gradient); - border-radius: inherit; - transform: scale(0); - transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1); } .scroll-dot.active { - width: 10px; - height: 24px; - border-radius: 12px; + height: 20px; + background: var(--ink); opacity: 1; } - .scroll-dot.active::before { - transform: scale(1); - } - - /* Slide-out navigation menu */ + /* ── Slide-out Navigation Menu ──────────────────────── */ .scroll-menu { position: fixed; - right: 3rem; + right: var(--sp-8); top: 50%; - transform: translateY(-50%) translateX(10px); - background: var(--glass-primary); - backdrop-filter: blur(25px); - -webkit-backdrop-filter: blur(25px); - border: 1px solid var(--glass-border); - border-radius: 16px; - padding: 1rem; - min-width: 200px; + transform: translateY(-50%) translateX(8px); + background: var(--bg); + border: 1px solid var(--line); + border-radius: 12px; + padding: var(--sp-3); + min-width: 172px; opacity: 0; visibility: hidden; - transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition: + opacity var(--t-base) var(--ease-out), + transform var(--t-base) var(--ease-out), + visibility var(--t-base); z-index: 999; - box-shadow: var(--glass-shadow); } .scroll-menu.active { @@ -260,67 +198,57 @@ .scroll-menu-item { display: flex; align-items: center; - gap: 1rem; - padding: 0.75rem 1rem; - border-radius: 12px; + gap: var(--sp-3); + padding: var(--sp-3) var(--sp-4); + border-radius: 8px; + color: var(--ink-2); + transition: background var(--t-fast), color var(--t-fast); cursor: pointer; - transition: var(--transition-fast); - color: var(--text-color); - font-size: 0.9rem; - font-weight: 500; - margin-bottom: 0.5rem; - } - - .scroll-menu-item:last-child { - margin-bottom: 0; + -webkit-tap-highlight-color: transparent; } - .scroll-menu-item:hover { - background: var(--glass-secondary); - transform: translateX(2px); + .scroll-menu-item:not(:last-child) { + margin-bottom: 2px; } + .scroll-menu-item:hover, .scroll-menu-item.active { - background: var(--glass-secondary); + background: var(--accent-sub); + color: var(--ink); } - .scroll-menu-item.active .scroll-menu-icon { - background: var(--gradient-colors); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-size: 200% 200%; - animation: textGradient 8s ease infinite; + .scroll-menu-num { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-3); + letter-spacing: 0.04em; + flex-shrink: 0; } - .scroll-menu-icon { - font-size: 1.1rem; - width: 20px; - text-align: center; - transition: var(--transition-fast); + .scroll-menu-item.active .scroll-menu-num { + color: var(--accent); } .scroll-menu-label { - flex: 1; - opacity: 0.9; + font-family: var(--font-body); + font-size: var(--text-sm); + font-weight: 400; } .scroll-menu-item.active .scroll-menu-label { - opacity: 1; - font-weight: 600; + font-weight: 500; } - /* Menu backdrop */ + /* ── Backdrop ───────────────────────────────────────── */ .scroll-menu-backdrop { position: fixed; inset: 0; - background: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(2px); - -webkit-backdrop-filter: blur(2px); z-index: 998; opacity: 0; visibility: hidden; - transition: var(--transition-smooth); + transition: + opacity var(--t-base), + visibility var(--t-base); } .scroll-menu-backdrop.active { @@ -328,109 +256,64 @@ visibility: visible; } - /* Scroll hint */ + /* ── Scroll Hint ────────────────────────────────────── */ .scroll-hint { position: fixed; - bottom: 2rem; - left: 50%; - transform: translateX(-50%) translateY(10px); - background: var(--glass-primary); - backdrop-filter: blur(25px); - border: 1px solid var(--glass-border); - border-radius: 20px; - padding: 0.8rem 1.2rem; - font-size: 0.8rem; - color: var(--text-color); + bottom: var(--sp-8); + left: var(--sp-8); display: flex; align-items: center; - gap: 0.5rem; + gap: var(--sp-3); opacity: 0; - animation: scrollHintFade 4s ease-in-out 1s; + animation: hintFade 4s ease-in-out 1.8s; pointer-events: none; z-index: 999; } - .scroll-hint i { - animation: bounce 2s infinite; - } - - /* Animations */ - @keyframes mobileCardIn { - from { - opacity: 0; - transform: translateY(30px); - } - to { - opacity: 1; - transform: translateY(0); - } - } - - @keyframes scrollHintFade { - 0%, - 100% { - opacity: 0; - transform: translateX(-50%) translateY(10px); - } - 25%, - 75% { - opacity: 1; - transform: translateX(-50%) translateY(0); - } - } - - @keyframes bounce { - 0%, - 20%, - 50%, - 80%, - 100% { - transform: translateY(0); - } - 40% { - transform: translateY(-5px); - } - 60% { - transform: translateY(-3px); - } - } - - @keyframes bounceStart { - 0% { - transform: translateY(0); - } - 50% { - transform: translateY(10px); - } - 100% { - transform: translateY(0); - } - } - - @keyframes bounceEnd { - 0% { - transform: translateY(0); - } - 50% { - transform: translateY(-10px); - } - 100% { - transform: translateY(0); - } - } - - /* Bounce effect classes */ + .scroll-hint-line { + width: 20px; + height: 1px; + background: var(--ink-3); + } + + .scroll-hint span { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--ink-3); + text-transform: uppercase; + letter-spacing: 0.12em; + } + + /* ── Bounce Feedback ────────────────────────────────── */ .mobile-portrait-layout.bounce-start { - animation: bounceStart 0.3s ease-out; + animation: bounceDown 0.3s ease-out; } .mobile-portrait-layout.bounce-end { - animation: bounceEnd 0.3s ease-out; + animation: bounceUp 0.3s ease-out; + } + + /* ── Keyframes ──────────────────────────────────────── */ + @keyframes hintFade { + 0%, 100% { opacity: 0; transform: translateY(6px); } + 25%, 75% { opacity: 1; transform: translateY(0); } + } + + @keyframes bounceDown { + 0% { transform: translateY(0); } + 50% { transform: translateY(8px); } + 100% { transform: translateY(0); } + } + + @keyframes bounceUp { + 0% { transform: translateY(0); } + 50% { transform: translateY(-8px); } + 100% { transform: translateY(0); } } - /* Focus states for accessibility */ - .scroll-menu-item:focus { - outline: 2px solid var(--glass-border); + /* ── Accessibility ──────────────────────────────────── */ + .scroll-menu-item:focus-visible { + outline: 2px solid var(--accent); outline-offset: 2px; } } diff --git a/index.html b/index.html index 71272d2..f74fe25 100755 --- a/index.html +++ b/index.html @@ -3,153 +3,154 @@ - Brad Cooley | Software & Data Engineer + Brad Cooley — Software & Data Engineer + + - + - + + + + + + + + +
+ +
- + +
-
-
-
-

Brad Cooley

-
-

Software & Data Engineer

-
- +
-
-
-
- -
-

About

-

+

+
+ +
+

Passionate developer creating elegant solutions to complex problems. Focused on building applications that provide exceptional user experiences.

-
- - More details coming soon -
+ — More details in progress
+
-
-
-
- -
-

Projects

-

- Explore my latest work, from web applications to open-source - contributions. Each project represents a journey of learning, - problem-solving, and innovation. +

+
+ +
+

+ A selection of work spanning web applications, data + engineering, and open-source contributions. Each project + represents a journey of learning and craft.

-
- - Showcase launching soon -
+ — Showcase launching soon
+
-
-
-
- -
-

Blog

-

- Dive into my thoughts on technology, development practices, - and industry trends. Knowledge sharing and documentation of my - learning journey. +

+
+ +
+

+ Thoughts on technology, development practices, and the craft + of building software. A documentation of my learning journey.

-
- - First posts coming soon -
+ — First posts arriving soon
+
-
-
-
- -
-

Resume

-

- Professional background, skills, and experience. A - comprehensive look at my career journey and technical - expertise. +

+
+ +
+

+ Professional background, technical skills, and experience. A + comprehensive look at my career journey as a software and + data engineer.

-
- - Full resume coming soon -
+ — Full resume arriving soon
-
- -
+ + + -
+ + - +
+ +
-
- -

Brad Cooley

-

Software & Data Engineer

- - +
-
- -

About

-

+

+
02 / 05
+

About

+
+

Passionate developer creating elegant solutions to complex problems. Focused on building applications that provide exceptional user experiences.

-
- - More details coming soon -
+ — More details in progress
+
-
- -

Projects

-

- Explore my latest work, from web applications to open-source - contributions. Each project represents a journey of learning and - innovation. +

+
03 / 05
+

Projects

+
+

+ A selection of work spanning web applications, data engineering, + and open-source contributions.

-
- - Showcase launching soon -
+ — Showcase launching soon
+
-
- -

Blog

-

- Dive into my thoughts on technology, development practices, and - industry trends. Knowledge sharing and learning journey - documentation. +

+
04 / 05
+

Writing

+
+

+ Thoughts on technology, development practices, and the craft of + building software.

-
- - First posts coming soon -
+ — First posts arriving soon
+
-
- -

Resume

-

- Professional background, skills, and experience. A comprehensive - look at my career journey and technical expertise. +

+
05 / 05
+

Resume

+
+

+ Professional background, technical skills, and experience.

-
- - Full resume coming soon -
+ — Full resume arriving soon
-
+ +
@@ -298,63 +293,41 @@

Resume

- -
-
- + +
- - Scroll to explore +
+ Scroll
-
-
+ +
+ +
+ diff --git a/js/main.js b/js/main.js index 45129ea..582b544 100644 --- a/js/main.js +++ b/js/main.js @@ -77,6 +77,8 @@ class PortfolioNavigator { } else { this.goToDesktopSection(index, smoothTransition); } + + this.updateProgressBar(); } goToDesktopSection(index, smoothTransition = true) { @@ -246,9 +248,10 @@ class PortfolioNavigator { this.state.initialTransform = this.getTransformX(); if (this.elements.sectionsContainer) { - this.elements.sectionsContainer.style.cursor = "grabbing"; this.elements.sectionsContainer.classList.add("no-transition"); } + + document.querySelector(".cursor")?.classList.add("is-dragging"); } updateDrag(clientX) { @@ -270,10 +273,11 @@ class PortfolioNavigator { this.state.isDragging = false; if (this.elements.sectionsContainer) { - this.elements.sectionsContainer.style.cursor = "grab"; this.elements.sectionsContainer.classList.remove("no-transition"); } + document.querySelector(".cursor")?.classList.remove("is-dragging"); + const deltaX = this.state.currentX - this.state.startX; const threshold = window.innerWidth * NAVIGATION_CONFIG.DRAG_THRESHOLD; @@ -520,6 +524,58 @@ class PortfolioNavigator { ); } + // Custom cursor tracking (fine-pointer / mouse only) + initCursor() { + const cursor = document.querySelector(".cursor"); + if (!cursor || !window.matchMedia("(pointer: fine)").matches) return; + + let rafPending = false; + let x = window.innerWidth / 2; + let y = window.innerHeight / 2; + + document.addEventListener("mousemove", (e) => { + x = e.clientX; + y = e.clientY; + if (!rafPending) { + rafPending = true; + requestAnimationFrame(() => { + cursor.style.transform = `translate(calc(${x}px - 50%), calc(${y}px - 50%))`; + rafPending = false; + }); + } + }); + + document.addEventListener("mouseleave", () => { + cursor.style.opacity = "0"; + }); + document.addEventListener("mouseenter", () => { + cursor.style.opacity = "1"; + }); + + const hoverTargets = document.querySelectorAll( + "a, button, [role='button'], .nav-item, .scroll-dot, .scroll-menu-item" + ); + hoverTargets.forEach((el) => { + el.addEventListener("mouseenter", () => + cursor.classList.add("is-hovering") + ); + el.addEventListener("mouseleave", () => + cursor.classList.remove("is-hovering") + ); + }); + } + + // Thin progress bar across the top + updateProgressBar() { + const bar = document.getElementById("progressBar"); + if (!bar) return; + const pct = + this.totalSections > 1 + ? (this.state.currentSection / (this.totalSections - 1)) * 100 + : 0; + bar.style.width = pct + "%"; + } + // Initialization init() { const hash = location.hash.slice(1); @@ -527,6 +583,7 @@ class PortfolioNavigator { this.state.currentSection = idx !== -1 ? idx : 0; this.setupEventListeners(); + this.initCursor(); // Initialize after a brief delay to ensure DOM readiness setTimeout(() => { From c4e1712923611187279261bead61c553fd69a315 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 01:08:29 +0000 Subject: [PATCH 2/4] Remove progress bar; add animated color and polished theme toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Progress bar: removed from HTML, base.css, and main.js - Name animation: slow 14s colour-breathing cycle (ink → accent → ink) on both desktop h1 and mobile name; animation pauses during theme switch so colour transitions cleanly instead of jumping - Section titles: accent colour (--accent) on h2 for consistent colour pop across all non-home sections on desktop and mobile - Theme toggle: unique CSS half-circle that rotates 180° between modes (left-half filled = light, right-half filled = dark); fixed in nav on desktop, bottom-left on mobile - Theme init: inline script sets data-theme before first paint to prevent flash; preference persisted in localStorage - Transition: .is-theme-transitioning class adds 500ms colour transitions to every element during the mode switch, then is removed https://claude.ai/code/session_01BkB715SdTfJR99BjBzB1N4 --- css/base.css | 59 ++++++++++++++++++++++++++++---------- css/desktop.css | 63 +++++++++++++++++++++++++++++++++++++++-- css/mobile-portrait.css | 48 ++++++++++++++++++++++++++++++- index.html | 14 +++++++-- js/main.js | 44 +++++++++++++++++++--------- 5 files changed, 194 insertions(+), 34 deletions(-) diff --git a/css/base.css b/css/base.css index 54f1cbd..1cad9c2 100644 --- a/css/base.css +++ b/css/base.css @@ -56,9 +56,33 @@ --t-section: 600ms; } -/* ── Dark Mode ──────────────────────────────────────────── */ +/* ── Dark / Light via data-theme attribute ──────────────── */ +/* Applied before paint by inline script to prevent flash */ +[data-theme="light"] { + --bg: #F8F7F4; + --bg-raised: #FFFFFF; + --ink: #141412; + --ink-2: #6B6A67; + --ink-3: #B4B2AE; + --line: #E5E3DF; + --accent: #1246F0; + --accent-sub: rgba(18, 70, 240, 0.06); +} + +[data-theme="dark"] { + --bg: #0F0F0E; + --bg-raised: #1A1A18; + --ink: #F0EFE9; + --ink-2: #8A8883; + --ink-3: #555350; + --line: #2D2C2A; + --accent: #4F7BF7; + --accent-sub: rgba(79, 123, 247, 0.08); +} + +/* Fallback for browsers without JS / no stored preference */ @media (prefers-color-scheme: dark) { - :root { + :root:not([data-theme]) { --bg: #0F0F0E; --bg-raised: #1A1A18; --ink: #F0EFE9; @@ -70,6 +94,17 @@ } } +/* ── Smooth theme transition class ──────────────────────── */ +/* Added briefly by JS during theme toggle */ +.is-theme-transitioning, +.is-theme-transitioning * { + transition: + background-color 500ms cubic-bezier(0.16, 1, 0.3, 1), + color 500ms cubic-bezier(0.16, 1, 0.3, 1), + border-color 500ms cubic-bezier(0.16, 1, 0.3, 1), + box-shadow 500ms cubic-bezier(0.16, 1, 0.3, 1) !important; +} + /* ── Reset ──────────────────────────────────────────────── */ *, *::before, *::after { box-sizing: border-box; @@ -152,19 +187,6 @@ body { background-repeat: repeat; } -/* ── Progress Bar ───────────────────────────────────────── */ -.progress-bar { - position: fixed; - top: 0; - left: 0; - height: 1.5px; - background: var(--accent); - z-index: 10000; - width: 0%; - transition: width var(--t-section) var(--ease-out); - pointer-events: none; -} - /* ── Keyframes ──────────────────────────────────────────── */ @keyframes fadeUp { from { opacity: 0; transform: translateY(18px); } @@ -181,6 +203,13 @@ body { to { transform: scaleX(1); } } +/* Name breathing: mostly --ink, quietly visits --accent, returns */ +@keyframes nameColor { + 0%, 30% { color: var(--ink); } + 50% { color: var(--accent); } + 70%, 100% { color: var(--ink); } +} + /* ── Utility ────────────────────────────────────────────── */ .sr-only { position: absolute; diff --git a/css/desktop.css b/css/desktop.css index e4c6048..46bc4e9 100644 --- a/css/desktop.css +++ b/css/desktop.css @@ -82,7 +82,9 @@ line-height: 0.91; letter-spacing: -0.04em; color: var(--ink); - animation: fadeUp var(--t-enter) var(--ease-out) 0.1s both; + animation: + fadeUp var(--t-enter) var(--ease-out) 0.1s both, + nameColor 14s ease-in-out 2.5s infinite; } .home-rule { @@ -187,7 +189,7 @@ font-weight: 800; line-height: 0.95; letter-spacing: -0.03em; - color: var(--ink); + color: var(--accent); } .section-text-col { @@ -300,11 +302,66 @@ margin-left: auto; display: flex; align-items: center; - padding-right: clamp(var(--sp-8), 5vw, var(--sp-16)); + padding-right: var(--sp-12); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--ink-3); letter-spacing: 0.08em; user-select: none; } + + /* ── Theme Toggle — desktop positioning ─────────────── */ + .theme-toggle { + position: fixed; + bottom: 0; + right: clamp(var(--sp-8), 5vw, var(--sp-16)); + height: 56px; + width: 40px; + z-index: 1001; + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + cursor: none; + padding: 0; + flex-shrink: 0; + } + + .theme-toggle-icon { + width: 16px; + height: 16px; + border-radius: 50%; + border: 1.5px solid var(--ink-3); + position: relative; + overflow: hidden; + transition: + transform 500ms cubic-bezier(0.16, 1, 0.3, 1), + border-color var(--t-base) var(--ease-out); + } + + /* Left half filled = light-mode indicator at rest */ + .theme-toggle-icon::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 50%; + height: 100%; + background: var(--ink-3); + transition: background var(--t-base) var(--ease-out); + } + + /* Dark mode: rotate 180° so filled half is on the right */ + [data-theme="dark"] .theme-toggle-icon { + transform: rotate(180deg); + } + + .theme-toggle:hover .theme-toggle-icon { + border-color: var(--ink); + } + + .theme-toggle:hover .theme-toggle-icon::before { + background: var(--ink); + } } diff --git a/css/mobile-portrait.css b/css/mobile-portrait.css index fb38bef..93c8982 100644 --- a/css/mobile-portrait.css +++ b/css/mobile-portrait.css @@ -117,7 +117,12 @@ font-weight: 800; line-height: 0.95; letter-spacing: -0.03em; - color: var(--ink); + color: var(--accent); + } + + /* Home name breathes in colour on mobile too */ + .mobile-name { + animation: nameColor 14s ease-in-out 2.5s infinite; } .mobile-body-text { @@ -311,6 +316,47 @@ 100% { transform: translateY(0); } } + /* ── Theme Toggle — mobile positioning ─────────────── */ + .theme-toggle { + position: fixed; + bottom: var(--sp-8); + left: var(--sp-8); + width: 36px; + height: 36px; + z-index: 1001; + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + padding: 0; + -webkit-tap-highlight-color: transparent; + } + + .theme-toggle-icon { + width: 16px; + height: 16px; + border-radius: 50%; + border: 1.5px solid var(--ink-3); + position: relative; + overflow: hidden; + transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1); + } + + .theme-toggle-icon::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 50%; + height: 100%; + background: var(--ink-3); + } + + [data-theme="dark"] .theme-toggle-icon { + transform: rotate(180deg); + } + /* ── Accessibility ──────────────────────────────────── */ .scroll-menu-item:focus-visible { outline: 2px solid var(--accent); diff --git a/index.html b/index.html index f74fe25..aefa609 100755 --- a/index.html +++ b/index.html @@ -16,6 +16,14 @@ + + @@ -24,8 +32,10 @@ - - + +
diff --git a/js/main.js b/js/main.js index 582b544..4b337fe 100644 --- a/js/main.js +++ b/js/main.js @@ -77,8 +77,6 @@ class PortfolioNavigator { } else { this.goToDesktopSection(index, smoothTransition); } - - this.updateProgressBar(); } goToDesktopSection(index, smoothTransition = true) { @@ -524,6 +522,36 @@ class PortfolioNavigator { ); } + // Theme toggle: polished half-circle flip + smooth color transition + initTheme() { + const toggle = document.getElementById("themeToggle"); + if (!toggle) return; + + toggle.addEventListener("click", () => { + const root = document.documentElement; + const current = root.getAttribute("data-theme"); + const next = current === "dark" ? "light" : "dark"; + + // Pause name animation during transition so colour transitions cleanly + const h1 = document.querySelector(".home-name h1"); + const mName = document.querySelector(".mobile-name"); + if (h1) h1.style.animationPlayState = "paused"; + if (mName) mName.style.animationPlayState = "paused"; + + // Enable global colour transitions for every element + root.classList.add("is-theme-transitioning"); + root.setAttribute("data-theme", next); + localStorage.setItem("theme", next); + + // Remove transition class and resume animation after colours settle + setTimeout(() => { + root.classList.remove("is-theme-transitioning"); + if (h1) h1.style.animationPlayState = ""; + if (mName) mName.style.animationPlayState = ""; + }, 560); + }); + } + // Custom cursor tracking (fine-pointer / mouse only) initCursor() { const cursor = document.querySelector(".cursor"); @@ -565,17 +593,6 @@ class PortfolioNavigator { }); } - // Thin progress bar across the top - updateProgressBar() { - const bar = document.getElementById("progressBar"); - if (!bar) return; - const pct = - this.totalSections > 1 - ? (this.state.currentSection / (this.totalSections - 1)) * 100 - : 0; - bar.style.width = pct + "%"; - } - // Initialization init() { const hash = location.hash.slice(1); @@ -583,6 +600,7 @@ class PortfolioNavigator { this.state.currentSection = idx !== -1 ? idx : 0; this.setupEventListeners(); + this.initTheme(); this.initCursor(); // Initialize after a brief delay to ensure DOM readiness From ae92ffcc44e525526ca7bde2f71396c511fd6b14 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 01:30:19 +0000 Subject: [PATCH 3/4] Cosmic Orange gradient sweep on name; stronger theme toggle animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Name gradient: - background-clip: text with 200% wide gradient: --bg → --ink → Cosmic Orange (#BF4800 → #F07218 → #BF4800) → --ink → --bg - Edges fade to --bg so the name blends into the background colour - nameShimmer keyframe animates background-position 100%→0%→100% on an 18s ease-in-out loop: orange sweeps left→right, holds, returns - Applied identically on desktop (h1) and mobile (.mobile-name) Theme toggle: - Icon size: 16px → 18px, border colour: ink-3 → ink-2 (more visible) - Rotation now uses individual CSS `rotate` property; scale uses `scale` property — they animate independently so the pulse and flip never conflict (togglePulse keyframe: 0→1.5→1 in 360ms spring) - Flash overlay: directional tint (#fff when going light, #000 dark) fades in to 12% opacity, theme switch fires at peak, then fades out creating a physical "shutter" sensation - Transition class applied at peak flash for 700ms colour settle https://claude.ai/code/session_01BkB715SdTfJR99BjBzB1N4 --- css/base.css | 14 +++++++++++ css/desktop.css | 46 ++++++++++++++++++++++++++--------- css/mobile-portrait.css | 36 +++++++++++++++++++++------- js/main.js | 53 ++++++++++++++++++++++++++++------------- 4 files changed, 114 insertions(+), 35 deletions(-) diff --git a/css/base.css b/css/base.css index 1cad9c2..2b610d7 100644 --- a/css/base.css +++ b/css/base.css @@ -210,6 +210,20 @@ body { 70%, 100% { color: var(--ink); } } +/* Cosmic Orange gradient sweeps left→right, holds, returns */ +@keyframes nameShimmer { + 0%, 8% { background-position: 100% center; } + 40%, 60% { background-position: 0% center; } + 92%, 100% { background-position: 100% center; } +} + +/* Toggle icon scale pulse on click */ +@keyframes togglePulse { + 0% { scale: 1; } + 40% { scale: 1.5; } + 100% { scale: 1; } +} + /* ── Utility ────────────────────────────────────────────── */ .sr-only { position: absolute; diff --git a/css/desktop.css b/css/desktop.css index 46bc4e9..8ca34c0 100644 --- a/css/desktop.css +++ b/css/desktop.css @@ -81,10 +81,27 @@ font-weight: 800; line-height: 0.91; letter-spacing: -0.04em; - color: var(--ink); + /* Cosmic Orange gradient — fades to --bg at edges so name blends + with background; orange sweeps left→right via nameShimmer */ + background: linear-gradient( + 90deg, + var(--bg) 0%, + var(--ink) 10%, + var(--ink) 34%, + #BF4800 43%, + #F07218 50%, + #BF4800 57%, + var(--ink) 66%, + var(--ink) 90%, + var(--bg) 100% + ); + background-size: 200% 100%; + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; animation: - fadeUp var(--t-enter) var(--ease-out) 0.1s both, - nameColor 14s ease-in-out 2.5s infinite; + fadeUp var(--t-enter) var(--ease-out) 0.1s both, + nameShimmer 18s ease-in-out 4s infinite; } .home-rule { @@ -329,15 +346,17 @@ } .theme-toggle-icon { - width: 16px; - height: 16px; + width: 18px; + height: 18px; border-radius: 50%; - border: 1.5px solid var(--ink-3); + border: 1.5px solid var(--ink-2); position: relative; overflow: hidden; + /* rotate and scale are individual transform properties — + they animate independently so pulse and flip don't conflict */ transition: - transform 500ms cubic-bezier(0.16, 1, 0.3, 1), - border-color var(--t-base) var(--ease-out); + rotate 600ms cubic-bezier(0.16, 1, 0.3, 1), + border-color var(--t-base) var(--ease-out); } /* Left half filled = light-mode indicator at rest */ @@ -348,13 +367,18 @@ left: 0; width: 50%; height: 100%; - background: var(--ink-3); + background: var(--ink-2); transition: background var(--t-base) var(--ease-out); } - /* Dark mode: rotate 180° so filled half is on the right */ + /* Dark mode: flip so filled half is on the right */ [data-theme="dark"] .theme-toggle-icon { - transform: rotate(180deg); + rotate: 180deg; + } + + /* Scale pulse triggered by JS on click */ + .theme-toggle-icon.is-pulsing { + animation: togglePulse 360ms cubic-bezier(0.34, 1.56, 0.64, 1); } .theme-toggle:hover .theme-toggle-icon { diff --git a/css/mobile-portrait.css b/css/mobile-portrait.css index 93c8982..64bfda9 100644 --- a/css/mobile-portrait.css +++ b/css/mobile-portrait.css @@ -120,9 +120,25 @@ color: var(--accent); } - /* Home name breathes in colour on mobile too */ + /* Cosmic Orange gradient sweep — mirrors desktop */ .mobile-name { - animation: nameColor 14s ease-in-out 2.5s infinite; + background: linear-gradient( + 90deg, + var(--bg) 0%, + var(--ink) 10%, + var(--ink) 34%, + #BF4800 43%, + #F07218 50%, + #BF4800 57%, + var(--ink) 66%, + var(--ink) 90%, + var(--bg) 100% + ); + background-size: 200% 100%; + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + animation: nameShimmer 18s ease-in-out 4s infinite; } .mobile-body-text { @@ -334,13 +350,13 @@ } .theme-toggle-icon { - width: 16px; - height: 16px; + width: 18px; + height: 18px; border-radius: 50%; - border: 1.5px solid var(--ink-3); + border: 1.5px solid var(--ink-2); position: relative; overflow: hidden; - transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1); + transition: rotate 600ms cubic-bezier(0.16, 1, 0.3, 1); } .theme-toggle-icon::before { @@ -350,11 +366,15 @@ left: 0; width: 50%; height: 100%; - background: var(--ink-3); + background: var(--ink-2); } [data-theme="dark"] .theme-toggle-icon { - transform: rotate(180deg); + rotate: 180deg; + } + + .theme-toggle-icon.is-pulsing { + animation: togglePulse 360ms cubic-bezier(0.34, 1.56, 0.64, 1); } /* ── Accessibility ──────────────────────────────────── */ diff --git a/js/main.js b/js/main.js index 4b337fe..5f1b68e 100644 --- a/js/main.js +++ b/js/main.js @@ -522,7 +522,7 @@ class PortfolioNavigator { ); } - // Theme toggle: polished half-circle flip + smooth color transition + // Theme toggle: half-circle flip, scale pulse, flash overlay initTheme() { const toggle = document.getElementById("themeToggle"); if (!toggle) return; @@ -532,23 +532,44 @@ class PortfolioNavigator { const current = root.getAttribute("data-theme"); const next = current === "dark" ? "light" : "dark"; - // Pause name animation during transition so colour transitions cleanly - const h1 = document.querySelector(".home-name h1"); - const mName = document.querySelector(".mobile-name"); - if (h1) h1.style.animationPlayState = "paused"; - if (mName) mName.style.animationPlayState = "paused"; + const icon = toggle.querySelector(".theme-toggle-icon"); - // Enable global colour transitions for every element - root.classList.add("is-theme-transitioning"); - root.setAttribute("data-theme", next); - localStorage.setItem("theme", next); + // Scale-pulse the icon on click (scale and rotate animate independently) + if (icon) { + icon.classList.remove("is-pulsing"); + void icon.offsetWidth; // force reflow to restart animation + icon.classList.add("is-pulsing"); + icon.addEventListener("animationend", () => + icon.classList.remove("is-pulsing"), { once: true } + ); + } - // Remove transition class and resume animation after colours settle - setTimeout(() => { - root.classList.remove("is-theme-transitioning"); - if (h1) h1.style.animationPlayState = ""; - if (mName) mName.style.animationPlayState = ""; - }, 560); + // Brief directional flash — white going to light, black going to dark + const flash = document.createElement("div"); + flash.style.cssText = [ + "position:fixed", "inset:0", + `background:${next === "light" ? "#fff" : "#000"}`, + "pointer-events:none", "z-index:99997", "opacity:0", + "transition:opacity 180ms ease-out", + ].join(";"); + document.body.appendChild(flash); + + requestAnimationFrame(() => { + flash.style.opacity = "0.12"; + + // Apply theme at peak flash so colour change feels instantaneous + setTimeout(() => { + root.classList.add("is-theme-transitioning"); + root.setAttribute("data-theme", next); + localStorage.setItem("theme", next); + + flash.style.opacity = "0"; + setTimeout(() => flash.remove(), 200); + }, 90); + }); + + // Remove transition class after colours have settled + setTimeout(() => root.classList.remove("is-theme-transitioning"), 760); }); } From d5c34ee08b66115614ace75e054d35788e7212a8 Mon Sep 17 00:00:00 2001 From: Brad Cooley Date: Sat, 18 Apr 2026 23:24:26 -0400 Subject: [PATCH 4/4] new design --- css/base.css | 27 +++++---- css/desktop.css | 79 ++++++++++++------------ css/mobile-portrait.css | 129 +++++++++++++++++++++++++--------------- index.html | 117 ++++++++++++++++++++++++------------ js/main.js | 88 +++++++++++++++------------ 5 files changed, 267 insertions(+), 173 deletions(-) diff --git a/css/base.css b/css/base.css index 2b610d7..9fae2d7 100644 --- a/css/base.css +++ b/css/base.css @@ -12,8 +12,11 @@ --ink-2: #6B6A67; --ink-3: #B4B2AE; --line: #E5E3DF; - --accent: #1246F0; - --accent-sub: rgba(18, 70, 240, 0.06); + --accent: #1246F0; + --accent-sub: rgba(18, 70, 240, 0.06); + --cosmic-orange: #F07218; + --cosmic-orange-deep: #BF4800; + --section-title-color: var(--cosmic-orange); /* Typography */ --font-display: 'Syne', sans-serif; @@ -67,6 +70,10 @@ --line: #E5E3DF; --accent: #1246F0; --accent-sub: rgba(18, 70, 240, 0.06); + --cosmic-orange: #F07218; + --cosmic-orange-deep: #BF4800; + --cosmic-orange-sub: rgba(240, 114, 24, 0.08); + --section-title-color: var(--cosmic-orange); } [data-theme="dark"] { @@ -78,6 +85,10 @@ --line: #2D2C2A; --accent: #4F7BF7; --accent-sub: rgba(79, 123, 247, 0.08); + --cosmic-orange: #CC6420; + --cosmic-orange-deep: #A04200; + --cosmic-orange-sub: rgba(204, 100, 32, 0.10); + --section-title-color: var(--cosmic-orange-deep); } /* Fallback for browsers without JS / no stored preference */ @@ -91,6 +102,9 @@ --line: #2D2C2A; --accent: #4F7BF7; --accent-sub: rgba(79, 123, 247, 0.08); + --cosmic-orange: #CC6420; + --cosmic-orange-deep: #A04200; + --cosmic-orange-sub: rgba(204, 100, 32, 0.10); } } @@ -217,13 +231,6 @@ body { 92%, 100% { background-position: 100% center; } } -/* Toggle icon scale pulse on click */ -@keyframes togglePulse { - 0% { scale: 1; } - 40% { scale: 1.5; } - 100% { scale: 1; } -} - /* ── Utility ────────────────────────────────────────────── */ .sr-only { position: absolute; @@ -238,7 +245,7 @@ body { } :focus-visible { - outline: 2px solid var(--accent); + outline: 2px solid var(--cosmic-orange); outline-offset: 3px; border-radius: 2px; } diff --git a/css/desktop.css b/css/desktop.css index 8ca34c0..37c11da 100644 --- a/css/desktop.css +++ b/css/desktop.css @@ -4,7 +4,6 @@ ========================================================= */ @media (min-width: 768px) { - /* ── Layout ─────────────────────────────────────────── */ .mobile-portrait-layout { display: none; @@ -81,27 +80,25 @@ font-weight: 800; line-height: 0.91; letter-spacing: -0.04em; - /* Cosmic Orange gradient — fades to --bg at edges so name blends - with background; orange sweeps left→right via nameShimmer */ - background: linear-gradient( - 90deg, - var(--bg) 0%, - var(--ink) 10%, - var(--ink) 34%, - #BF4800 43%, - #F07218 50%, - #BF4800 57%, - var(--ink) 66%, - var(--ink) 90%, - var(--bg) 100% - ); - background-size: 200% 100%; + /* Cosmic Orange fades left→right into background; metallic noise + grain is layered on top via SVG feTurbulence, both clipped to text. + mask-image fades the whole rendering so noise opacity tracks the + color gradient — grain vanishes exactly where the color does. */ + background: + url("data:image/svg+xml,") + repeat, + linear-gradient( + to right, + var(--cosmic-orange) 0%, + var(--cosmic-orange-deep) 20%, + var(--bg) 94% + ); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; - animation: - fadeUp var(--t-enter) var(--ease-out) 0.1s both, - nameShimmer 18s ease-in-out 4s infinite; + -webkit-mask-image: linear-gradient(to right, black 0%, transparent 94%); + mask-image: linear-gradient(to right, black 0%, transparent 94%); + animation: fadeUp var(--t-enter) var(--ease-out) 0.1s both; } .home-rule { @@ -147,13 +144,13 @@ } .home-links a::after { - content: ''; + content: ""; position: absolute; bottom: 0; left: 0; width: 100%; height: 1px; - background: var(--accent); + background: var(--cosmic-orange); transform: scaleX(0); transform-origin: left; transition: transform var(--t-slow) var(--ease-out); @@ -206,7 +203,13 @@ font-weight: 800; line-height: 0.95; letter-spacing: -0.03em; - color: var(--accent); + background: + url("data:image/svg+xml,") + repeat, + var(--section-title-color); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; } .section-text-col { @@ -251,6 +254,17 @@ align-items: stretch; padding: 0 clamp(var(--sp-8), 5vw, var(--sp-16)); flex: 1; + position: relative; + } + + /* Sliding active indicator */ + .nav-indicator { + position: absolute; + top: -1px; + height: 2px; + background: var(--cosmic-orange); + transition: left var(--t-slow) var(--ease-out), width var(--t-slow) var(--ease-out); + pointer-events: none; } .nav-item { @@ -270,24 +284,20 @@ /* Animated top-edge indicator line */ .nav-item::before { - content: ''; + content: ""; position: absolute; top: -1px; left: 0; right: 0; height: 2px; - background: var(--ink); + background: var(--cosmic-orange); transform: scaleX(0); transform-origin: left; transition: transform var(--t-slow) var(--ease-out); } .nav-item.active { - color: var(--ink); - } - - .nav-item.active::before { - transform: scaleX(1); + color: var(--cosmic-orange); } .nav-item:hover:not(.active) { @@ -352,16 +362,14 @@ border: 1.5px solid var(--ink-2); position: relative; overflow: hidden; - /* rotate and scale are individual transform properties — - they animate independently so pulse and flip don't conflict */ transition: - rotate 600ms cubic-bezier(0.16, 1, 0.3, 1), + rotate var(--t-slow) cubic-bezier(0.16, 1, 0.3, 1), border-color var(--t-base) var(--ease-out); } /* Left half filled = light-mode indicator at rest */ .theme-toggle-icon::before { - content: ''; + content: ""; position: absolute; top: 0; left: 0; @@ -376,11 +384,6 @@ rotate: 180deg; } - /* Scale pulse triggered by JS on click */ - .theme-toggle-icon.is-pulsing { - animation: togglePulse 360ms cubic-bezier(0.34, 1.56, 0.64, 1); - } - .theme-toggle:hover .theme-toggle-icon { border-color: var(--ink); } diff --git a/css/mobile-portrait.css b/css/mobile-portrait.css index 64bfda9..8006819 100644 --- a/css/mobile-portrait.css +++ b/css/mobile-portrait.css @@ -4,7 +4,6 @@ ========================================================= */ @media (max-width: 767px) and (orientation: portrait) { - /* ── Layout ─────────────────────────────────────────── */ .desktop-layout { display: none; @@ -17,7 +16,6 @@ overflow-y: auto; overflow-x: hidden; scroll-snap-type: y mandatory; - scroll-behavior: smooth; -webkit-overflow-scrolling: touch; -ms-overflow-style: none; scrollbar-width: none; @@ -33,9 +31,9 @@ height: 100vh; width: 100vw; display: flex; - align-items: center; + align-items: flex-start; justify-content: flex-start; - padding: var(--sp-12) var(--sp-8); + padding: 22vh var(--sp-8) var(--sp-12); padding-right: calc(var(--sp-8) + 24px); scroll-snap-align: start; scroll-snap-stop: always; @@ -70,7 +68,7 @@ } .mobile-rule { - width: 100%; + width: 32px; height: 1px; background: var(--line); } @@ -117,28 +115,34 @@ font-weight: 800; line-height: 0.95; letter-spacing: -0.03em; - color: var(--accent); + background: + url("data:image/svg+xml,") + repeat, + var(--section-title-color); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; } - /* Cosmic Orange gradient sweep — mirrors desktop */ + /* Cosmic Orange fades left→right into background; metallic noise + grain layered via SVG feTurbulence, both clipped to text. + mask-image fades the whole rendering so noise opacity tracks the + color gradient — grain vanishes exactly where the color does. */ .mobile-name { - background: linear-gradient( - 90deg, - var(--bg) 0%, - var(--ink) 10%, - var(--ink) 34%, - #BF4800 43%, - #F07218 50%, - #BF4800 57%, - var(--ink) 66%, - var(--ink) 90%, - var(--bg) 100% - ); - background-size: 200% 100%; + background: + url("data:image/svg+xml,") + repeat, + linear-gradient( + to right, + var(--cosmic-orange) 0%, + var(--cosmic-orange-deep) 20%, + var(--bg) 94% + ); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; - animation: nameShimmer 18s ease-in-out 4s infinite; + -webkit-mask-image: linear-gradient(to right, black 0%, transparent 94%); + mask-image: linear-gradient(to right, black 0%, transparent 94%); } .mobile-body-text { @@ -172,21 +176,20 @@ } .scroll-dot { - width: 4px; - height: 4px; - border-radius: 99px; + width: 2px; + height: 2px; + border-radius: 0; background: var(--ink-3); transition: height 220ms var(--ease-out), - background 220ms var(--ease-out), - opacity 220ms; - opacity: 0.45; + background 220ms var(--ease-out); + opacity: 1; pointer-events: none; } .scroll-dot.active { height: 20px; - background: var(--ink); + background: var(--cosmic-orange); opacity: 1; } @@ -198,8 +201,8 @@ transform: translateY(-50%) translateX(8px); background: var(--bg); border: 1px solid var(--line); - border-radius: 12px; - padding: var(--sp-3); + border-radius: 0; + padding: var(--sp-2) 0; min-width: 172px; opacity: 0; visibility: hidden; @@ -221,43 +224,53 @@ align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); - border-radius: 8px; - color: var(--ink-2); - transition: background var(--t-fast), color var(--t-fast); + border-left: 2px solid transparent; + color: var(--ink-3); + transition: + border-color var(--t-fast), + color var(--t-fast); cursor: pointer; -webkit-tap-highlight-color: transparent; } .scroll-menu-item:not(:last-child) { - margin-bottom: 2px; + margin-bottom: 0; } - .scroll-menu-item:hover, - .scroll-menu-item.active { - background: var(--accent-sub); + .scroll-menu-item:hover { + background: none; + border-left-color: var(--cosmic-orange); color: var(--ink); } + .scroll-menu-item.active { + background: none; + border-left-color: var(--cosmic-orange); + } + .scroll-menu-num { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--ink-3); letter-spacing: 0.04em; flex-shrink: 0; + transition: color var(--t-fast); } .scroll-menu-item.active .scroll-menu-num { - color: var(--accent); + color: var(--cosmic-orange); } .scroll-menu-label { font-family: var(--font-body); font-size: var(--text-sm); font-weight: 400; + transition: color var(--t-fast); } .scroll-menu-item.active .scroll-menu-label { font-weight: 500; + color: var(--cosmic-orange); } /* ── Backdrop ───────────────────────────────────────── */ @@ -316,20 +329,40 @@ /* ── Keyframes ──────────────────────────────────────── */ @keyframes hintFade { - 0%, 100% { opacity: 0; transform: translateY(6px); } - 25%, 75% { opacity: 1; transform: translateY(0); } + 0%, + 100% { + opacity: 0; + transform: translateY(6px); + } + 25%, + 75% { + opacity: 1; + transform: translateY(0); + } } @keyframes bounceDown { - 0% { transform: translateY(0); } - 50% { transform: translateY(8px); } - 100% { transform: translateY(0); } + 0% { + transform: translateY(0); + } + 50% { + transform: translateY(8px); + } + 100% { + transform: translateY(0); + } } @keyframes bounceUp { - 0% { transform: translateY(0); } - 50% { transform: translateY(-8px); } - 100% { transform: translateY(0); } + 0% { + transform: translateY(0); + } + 50% { + transform: translateY(-8px); + } + 100% { + transform: translateY(0); + } } /* ── Theme Toggle — mobile positioning ─────────────── */ @@ -360,7 +393,7 @@ } .theme-toggle-icon::before { - content: ''; + content: ""; position: absolute; top: 0; left: 0; @@ -379,7 +412,7 @@ /* ── Accessibility ──────────────────────────────────── */ .scroll-menu-item:focus-visible { - outline: 2px solid var(--accent); + outline: 2px solid var(--cosmic-orange); outline-offset: 2px; } } diff --git a/index.html b/index.html index aefa609..7736759 100755 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + @@ -19,9 +19,12 @@ @@ -33,41 +36,45 @@ -
-
-
-

Brad
Cooley

+

Brad
Cooley

@@ -79,7 +86,7 @@

Brad
Cooley

@@ -98,7 +105,7 @@

Who
I am.

@@ -117,7 +124,7 @@

What
I build.

@@ -135,24 +142,25 @@

How
I think.

Professional background, technical skills, and experience. A - comprehensive look at my career journey as a software and - data engineer. + comprehensive look at my career journey as a software and data + engineer.

— Full resume arriving soon
- -
+
+ -
- +
+
-
01 / 05
-

Brad
Cooley

+

Brad
Cooley

-

Software & Data Engineer

+

Lead Data Engineer @ Mutually Human

@@ -295,7 +303,11 @@

Resume

-
+
@@ -304,24 +316,53 @@

Resume

-