diff --git a/css/screen.css b/css/screen.css index b4d94f3..55b5c33 100644 --- a/css/screen.css +++ b/css/screen.css @@ -12,6 +12,7 @@ /* Light Theme - Kami Warm Parchment Canvas & Ink Blue Accent */ --bg-page: #f5f4ed; /* Warm parchment canvas */ --bg-card: #faf9f5; /* Warm ivory container */ + --bg-card-rgb: 250, 249, 245; --bg-subtle: #e8e6dc; /* Warm sand interactive surface */ --bg-code: #1c1c1a; /* Deep warm charcoal for code */ --border-color: #e8e6dc; /* Primary warm border */ @@ -43,6 +44,7 @@ /* Dark Theme - Kami Dark Surface & Warm Charcoal */ --bg-page: #141413; /* Deep dark warm background */ --bg-card: #242422; /* Dark warm surface */ + --bg-card-rgb: 36, 36, 34; --bg-subtle: #2e2e2a; /* Dark warm sand */ --bg-code: #0f0f0e; /* Dark code background */ --border-color: rgba(232, 230, 220, 0.12); @@ -191,10 +193,26 @@ body { .hero-banner { position: relative; text-align: center; - padding: 5.5rem 0 5rem 0; + padding: 4rem 1rem; margin-bottom: 2.5rem; overflow: hidden; border-radius: var(--radius-lg); + background: var(--bg-card); + border: 1px solid var(--border-color); +} + +.hero-content { + position: relative; + z-index: 2; + max-width: 680px; + margin: 0 auto; + padding: 1.25rem 1.75rem; + background: rgba(var(--bg-card-rgb, 250, 249, 245), 0.85); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.03); } .hero-pretext-canvas { diff --git a/index.html b/index.html index 273ba83..b9e0e27 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,10 @@
+
+

Infrastructure, Systems & Engineering

+

Notes on Kubernetes, DevOps, Cloud Infrastructure, and Software Architecture.

+
diff --git a/js/hero-pretext.js b/js/hero-pretext.js index 17e2e17..2fdc725 100644 --- a/js/hero-pretext.js +++ b/js/hero-pretext.js @@ -122,28 +122,44 @@ const lineHeight = 22; let wordIdx = 0; - // Build list of obstacle circles from dragon segments - const obstacles = []; + // Build list of obstacles (dragon circles + DOM element rectangles) + const circularObstacles = []; if (dragon && dragon.segments) { // Head obstacle const head = dragon.segments[0]; - obstacles.push({ x: head.x, y: head.y, r: 48 }); + circularObstacles.push({ x: head.x, y: head.y, r: 48 }); // Body segment obstacles (every 3rd segment) for (let i = 2; i < dragon.segments.length; i += 3) { const seg = dragon.segments[i]; - obstacles.push({ x: seg.x, y: seg.y, r: Math.max(16, seg.width * 0.6) }); + circularObstacles.push({ x: seg.x, y: seg.y, r: Math.max(16, seg.width * 0.6) }); } } + // Query DOM obstacles inside container + const domObstacles = []; + const containerRect = container.getBoundingClientRect(); + const obstacleElements = container.querySelectorAll('.pretext-obstacle, .hero-title, .hero-subtitle'); + obstacleElements.forEach(el => { + const rect = el.getBoundingClientRect(); + const pad = 12; // padding around UI elements + const top = rect.top - containerRect.top - pad; + const bottom = rect.bottom - containerRect.top + pad; + const left = rect.left - containerRect.left - pad; + const right = rect.right - containerRect.left + pad; + if (right > 0 && left < width && bottom > 0 && top < height) { + domObstacles.push({ left, right, top, bottom }); + } + }); + for (let y = 18; y < height; y += lineHeight) { let x = 12; while (x < width - 12) { let availableSpan = width - 12 - x; - // Subtract dragon segment obstacles intersecting line at y - for (let obs of obstacles) { + // 1. Subtract dragon circular segment obstacles intersecting line at y + for (let obs of circularObstacles) { const dy = Math.abs(y - obs.y); if (dy < obs.r) { const dx = Math.sqrt(obs.r * obs.r - dy * dy); @@ -161,6 +177,20 @@ } } + // 2. Subtract DOM rectangular obstacles intersecting line at y + for (let obs of domObstacles) { + if (y >= obs.top && y <= obs.bottom) { + if (x < obs.right && x + availableSpan > obs.left) { + if (x < obs.left) { + availableSpan = obs.left - x; + } else { + x = obs.right; + availableSpan = width - 12 - x; + } + } + } + } + let count = 0; let currentSpan = 0; let checkIdx = wordIdx; @@ -173,7 +203,26 @@ } if (count === 0) { - x += 16; + // If phrase doesn't fit in gap, try CJK/character sub-chunk fitting + const phrase = phraseStream[wordIdx]; + let subFit = ""; + let subWidth = 0; + for (let char of phrase) { + const charWidth = ctx.measureText(char).width; + if (subWidth + charWidth <= availableSpan) { + subFit += char; + subWidth += charWidth; + } else { + break; + } + } + + if (subFit.length > 0 && availableSpan > 20) { + ctx.fillText(subFit, x, y); + x += subWidth + 8; + } else { + x += 16; + } continue; }