diff --git a/public/css/styles.css b/public/css/styles.css index 65504095f..11f3da7a9 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -100,6 +100,280 @@ .commands-gallery { display: block; } +.magazine-spread[data-category="diagnostic"] { + --spread-accent: oklch(55% 0.20 25); +} +.magazine-spread[data-category="quality"] { + --spread-accent: oklch(55% 0.20 160); +} +.magazine-spread[data-category="intensity"] { + --spread-accent: oklch(55% 0.22 350); +} +.magazine-spread[data-category="adaptation"] { + --spread-accent: oklch(55% 0.18 260); +} +.magazine-spread[data-category="enhancement"] { + --spread-accent: oklch(55% 0.20 300); +} +.magazine-spread[data-category="system"] { + --spread-accent: oklch(45% 0.05 0); +} +.magazine-container { + position: relative; + width: 100%; + display: grid; + grid-template-columns: 200px 1fr; + gap: var(--spacing-lg); + align-items: start; + overflow: hidden; +} +.fisheye-list { + position: relative; + height: 520px; + overflow: hidden; + mask-image: linear-gradient(to bottom, transparent, black 15%, black 85%, transparent); + -webkit-mask-image: linear-gradient(to bottom, transparent, black 15%, black 85%, transparent); +} +.fisheye-scroll { + height: 100%; + overflow-y: auto; + scrollbar-width: none; +} +.fisheye-scroll::-webkit-scrollbar { + display: none; +} +.fisheye-item { + display: block; + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 400; + color: var(--color-ink); + background: none; + border: none; + padding: 4px 0; + height: 39px; + cursor: pointer; + white-space: nowrap; + text-align: left; + width: 100%; + line-height: 1.3; + transform-origin: left center; + will-change: transform, opacity; +} +.fisheye-slash { + color: var(--color-mist); + font-weight: 300; +} +.fisheye-item.is-active { + font-weight: 600; +} +.fisheye-item.is-active .fisheye-slash { + color: var(--color-accent); +} +.magazine-viewport { + position: relative; + height: 520px; + overflow: hidden; +} +.magazine-spread { + position: absolute; + inset: 0; + display: grid; + grid-template-columns: 38% 1fr; + grid-template-rows: 1fr auto; + gap: 0 var(--spacing-xl); + padding: var(--spacing-lg) var(--spacing-lg); + opacity: 0; + pointer-events: none; + transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); + transform: translateY(16px); + will-change: opacity, transform; +} +.magazine-spread.active { + opacity: 1; + pointer-events: auto; + transform: translateY(0); + z-index: 2; +} +.magazine-spread.exiting { + opacity: 0; + transform: translateY(-16px); + z-index: 1; +} +.spread-identity { + grid-column: 1; + grid-row: 1 / 3; + display: flex; + flex-direction: column; + justify-content: center; + gap: var(--spacing-md); + padding-right: var(--spacing-lg); + border-right: 1px solid var(--color-mist); +} +.spread-category-label { + font-family: var(--font-body); + font-size: 0.6875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--spread-accent); +} +.spread-command-name { + font-family: var(--font-display); + font-size: clamp(3rem, 5.5vw, 5.5rem); + font-weight: 600; + line-height: 0.95; + color: var(--color-ink); + margin: 0; + letter-spacing: -0.02em; + word-break: break-word; + hyphens: auto; +} +.spread-command-name .spread-slash { + color: var(--spread-accent); + font-weight: 300; +} +.spread-command-name .beta-badge { + font-family: var(--font-body); + font-size: 0.55rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--spread-accent); + border: 1px solid var(--spread-accent); + border-radius: 3px; + padding: 2px 6px; + vertical-align: super; + margin-left: 8px; +} +.spread-description { + font-family: var(--font-body); + font-size: 0.9375rem; + line-height: 1.6; + color: var(--color-charcoal); + max-width: 360px; +} +.spread-flow { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; + margin-top: var(--spacing-xs); +} +.spread-flow-label { + font-family: var(--font-body); + font-size: 0.75rem; + color: var(--color-ash); +} +.spread-flow-icon { + color: var(--spread-accent); + font-weight: 600; + font-size: 0.8125rem; +} +.spread-flow-cmd { + font-family: var(--font-mono); + font-size: 0.6875rem; + background: var(--color-mist); + padding: 2px 7px; + border-radius: 3px; + color: var(--color-ink); +} +.spread-demo-area { + grid-column: 2; + grid-row: 1 / 3; + display: flex; + flex-direction: column; + min-height: 0; + overflow: hidden; +} +.spread-demo-area .demo-split-comparison { + display: flex; + flex-direction: column; + height: 100%; + flex: 1; + min-height: 0; +} +.spread-demo-area .demo-split-comparison .split-container { + position: relative; + flex: 1; + min-height: 0; + overflow: hidden; + cursor: ew-resize; + user-select: none; + background: var(--color-cream); +} +.spread-demo-area .demo-split-comparison .split-before, .spread-demo-area .demo-split-comparison .split-after { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: var(--spacing-md); +} +.spread-demo-area .demo-split-comparison .split-before { + z-index: 1; + background: var(--color-cream); +} +.spread-demo-area .demo-split-comparison .split-after { + z-index: 2; + background: var(--color-paper); + clip-path: polygon(58% 0%, 100% 0%, 100% 100%, 42% 100%); +} +.spread-demo-area .demo-split-comparison .split-content { + width: 100%; + max-width: 320px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.spread-demo-area .demo-split-comparison .split-divider { + position: absolute; + top: 0; + bottom: 0; + left: 50%; + width: 2px; + background: var(--spread-accent, var(--color-accent)); + transform: translateX(-50%) skewX(-10deg); + pointer-events: none; + z-index: 3; + box-shadow: 0 0 12px rgba(0,0,0,0.1); +} +.spread-demo-area .demo-split-comparison .split-label { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) skewX(10deg); + font-size: 0.5625rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--color-paper); + background: var(--spread-accent, var(--color-accent)); + padding: 4px 10px; + border-radius: 3px; + white-space: nowrap; +} +.spread-demo-area .demo-split-comparison .demo-caption { + flex-shrink: 0; + font-size: 0.75rem; + color: var(--color-ash); + text-align: center; + padding: var(--spacing-sm) var(--spacing-md); + border-top: 1px solid var(--color-mist); + background: var(--color-paper); +} +.spread-demo-area .demo-container { + flex: 1; + display: flex; + flex-direction: column; +} +.spread-demo-area .demo-container .demo-viewport { + flex: 1; + display: flex; + align-items: center; + justify-content: center; +} .commands-container { display: grid; grid-template-columns: 1fr 1.2fr; @@ -314,6 +588,12 @@ font-style: italic; } @media (max-width: 900px) { + .magazine-container { + display: none; + } + .fisheye-list { + display: none; + } .glass-terminal-wrapper { display: none; } diff --git a/public/css/workflow.css b/public/css/workflow.css index f75d90171..348ac51ef 100644 --- a/public/css/workflow.css +++ b/public/css/workflow.css @@ -8,9 +8,327 @@ .commands-gallery { /* Reset grid */ - display: block; + display: block; } +/* ============================================ + MAGAZINE SPREAD - Desktop Layout + ============================================ */ + +/* Category accent colors */ +.magazine-spread[data-category="diagnostic"] { --spread-accent: oklch(55% 0.20 25); } +.magazine-spread[data-category="quality"] { --spread-accent: oklch(55% 0.20 160); } +.magazine-spread[data-category="intensity"] { --spread-accent: oklch(55% 0.22 350); } +.magazine-spread[data-category="adaptation"] { --spread-accent: oklch(55% 0.18 260); } +.magazine-spread[data-category="enhancement"] { --spread-accent: oklch(55% 0.20 300); } +.magazine-spread[data-category="system"] { --spread-accent: oklch(45% 0.05 0); } + +.magazine-container { + position: relative; + width: 100%; + display: grid; + grid-template-columns: 200px 1fr; + gap: var(--spacing-lg); + align-items: start; + overflow: hidden; +} + +/* Fisheye command list */ +.fisheye-list { + position: relative; + height: 520px; + overflow: hidden; + mask-image: linear-gradient(to bottom, transparent, black 15%, black 85%, transparent); + -webkit-mask-image: linear-gradient(to bottom, transparent, black 15%, black 85%, transparent); +} + +.fisheye-scroll { + height: 100%; + overflow-y: auto; + scrollbar-width: none; + /* Padding set dynamically by JS based on container height */ +} + +.fisheye-scroll::-webkit-scrollbar { display: none; } + +.fisheye-item { + display: block; + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 400; + color: var(--color-ink); + background: none; + border: none; + /* Fixed height: 1.5rem * 1.3 line-height = 31.2px + 8px padding = ~39px */ + padding: 4px 0; + height: 39px; + cursor: pointer; + white-space: nowrap; + text-align: left; + width: 100%; + line-height: 1.3; + transform-origin: left center; + will-change: transform, opacity; +} + +.fisheye-slash { + color: var(--color-mist); + font-weight: 300; +} + +.fisheye-item.is-active { + font-weight: 600; +} + +.fisheye-item.is-active .fisheye-slash { + color: var(--color-accent); +} + +/* The viewport that clips to one spread at a time */ +.magazine-viewport { + position: relative; + height: 520px; + overflow: hidden; +} + +/* Individual spread */ +.magazine-spread { + position: absolute; + inset: 0; + display: grid; + grid-template-columns: 38% 1fr; + grid-template-rows: 1fr auto; + gap: 0 var(--spacing-xl); + padding: var(--spacing-lg) var(--spacing-lg); + opacity: 0; + pointer-events: none; + transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), + transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); + transform: translateY(16px); + will-change: opacity, transform; +} + +.magazine-spread.active { + opacity: 1; + pointer-events: auto; + transform: translateY(0); + z-index: 2; +} + +.magazine-spread.exiting { + opacity: 0; + transform: translateY(-16px); + z-index: 1; +} + +/* Left column: Big name + meta */ +.spread-identity { + grid-column: 1; + grid-row: 1 / 3; + display: flex; + flex-direction: column; + justify-content: center; + gap: var(--spacing-md); + padding-right: var(--spacing-lg); + border-right: 1px solid var(--color-mist); +} + +.spread-category-label { + font-family: var(--font-body); + font-size: 0.6875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--spread-accent); +} + +.spread-command-name { + font-family: var(--font-display); + font-size: clamp(3rem, 5.5vw, 5.5rem); + font-weight: 600; + line-height: 0.95; + color: var(--color-ink); + margin: 0; + letter-spacing: -0.02em; + word-break: break-word; + hyphens: auto; +} + +.spread-command-name .spread-slash { + color: var(--spread-accent); + font-weight: 300; +} + +.spread-command-name .beta-badge { + font-family: var(--font-body); + font-size: 0.55rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--spread-accent); + border: 1px solid var(--spread-accent); + border-radius: 3px; + padding: 2px 6px; + vertical-align: super; + margin-left: 8px; +} + +.spread-description { + font-family: var(--font-body); + font-size: 0.9375rem; + line-height: 1.6; + color: var(--color-charcoal); + max-width: 360px; +} + +/* Flow / relationship line */ +.spread-flow { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; + margin-top: var(--spacing-xs); +} + +.spread-flow-label { + font-family: var(--font-body); + font-size: 0.75rem; + color: var(--color-ash); +} + +.spread-flow-icon { + color: var(--spread-accent); + font-weight: 600; + font-size: 0.8125rem; +} + +.spread-flow-cmd { + font-family: var(--font-mono); + font-size: 0.6875rem; + background: var(--color-mist); + padding: 2px 7px; + border-radius: 3px; + color: var(--color-ink); +} + +/* Right column: Demo area */ +.spread-demo-area { + grid-column: 2; + grid-row: 1 / 3; + display: flex; + flex-direction: column; + min-height: 0; + overflow: hidden; +} + +/* Demo split comparison inside the spread */ +.spread-demo-area .demo-split-comparison { + display: flex; + flex-direction: column; + height: 100%; + flex: 1; + min-height: 0; +} + +.spread-demo-area .demo-split-comparison .split-container { + position: relative; + flex: 1; + min-height: 0; + overflow: hidden; + cursor: ew-resize; + user-select: none; + background: var(--color-cream); +} + +.spread-demo-area .demo-split-comparison .split-before, +.spread-demo-area .demo-split-comparison .split-after { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: var(--spacing-md); +} + +.spread-demo-area .demo-split-comparison .split-before { + z-index: 1; + background: var(--color-cream); +} + +.spread-demo-area .demo-split-comparison .split-after { + z-index: 2; + background: var(--color-paper); + clip-path: polygon(58% 0%, 100% 0%, 100% 100%, 42% 100%); +} + +.spread-demo-area .demo-split-comparison .split-content { + width: 100%; + max-width: 320px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.spread-demo-area .demo-split-comparison .split-divider { + position: absolute; + top: 0; + bottom: 0; + left: 50%; + width: 2px; + background: var(--spread-accent, var(--color-accent)); + transform: translateX(-50%) skewX(-10deg); + pointer-events: none; + z-index: 3; + box-shadow: 0 0 12px rgba(0,0,0,0.1); +} + +.spread-demo-area .demo-split-comparison .split-label { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) skewX(10deg); + font-size: 0.5625rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--color-paper); + background: var(--spread-accent, var(--color-accent)); + padding: 4px 10px; + border-radius: 3px; + white-space: nowrap; +} + +.spread-demo-area .demo-split-comparison .demo-caption { + flex-shrink: 0; + font-size: 0.75rem; + color: var(--color-ash); + text-align: center; + padding: var(--spacing-sm) var(--spacing-md); + border-top: 1px solid var(--color-mist); + background: var(--color-paper); +} + +/* Fallback demo container inside spread */ +.spread-demo-area .demo-container { + flex: 1; + display: flex; + flex-direction: column; +} + +.spread-demo-area .demo-container .demo-viewport { + flex: 1; + display: flex; + align-items: center; + justify-content: center; +} + +/* (nav moved to fisheye list) */ + +/* ============================================ + OLD DESKTOP STYLES (kept for mobile reuse) + ============================================ */ + .commands-container { display: grid; grid-template-columns: 1fr 1.2fr; @@ -264,9 +582,10 @@ } @media (max-width: 900px) { + .magazine-container { display: none; } + .fisheye-list { display: none; } .glass-terminal-wrapper { display: none; /* Hide on mobile for now, or stack */ - /* Alternatively: Position fixed bottom sheet */ } } diff --git a/public/js/components/glass-terminal.js b/public/js/components/glass-terminal.js index 55df7fc56..58af4a090 100644 --- a/public/js/components/glass-terminal.js +++ b/public/js/components/glass-terminal.js @@ -45,20 +45,41 @@ export function renderTerminalLayout(commands) { } // ============================================ -// DESKTOP LAYOUT (unchanged) +// DESKTOP LAYOUT - Magazine Spread // ============================================ -function renderDesktopLayout(container, commands) { - const categoryOrder = ['diagnostic', 'quality', 'intensity', 'adaptation', 'enhancement', 'system']; - const categoryLabels = { - 'diagnostic': 'Diagnose', - 'quality': 'Quality', - 'intensity': 'Intensity', - 'adaptation': 'Adaptation', - 'enhancement': 'Enhancement', - 'system': 'System' - }; +let magazineState = { + currentIndex: 0, + commands: [], + isTransitioning: false, + keyboardBound: false, + intersectionObserver: null +}; +const categoryOrder = ['diagnostic', 'quality', 'intensity', 'adaptation', 'enhancement', 'system']; +const categoryLabels = { + 'diagnostic': 'Diagnose', + 'quality': 'Quality', + 'intensity': 'Intensity', + 'adaptation': 'Adaptation', + 'enhancement': 'Enhancement', + 'system': 'System' +}; + +function renderDesktopLayout(container, commands) { + magazineState.commands = commands; + + // Determine starting index from URL hash + const hash = window.location.hash; + let startIndex = 0; + if (hash && hash.startsWith('#cmd-')) { + const cmdId = hash.slice(5); + const idx = commands.findIndex(c => c.id === cmdId); + if (idx >= 0) startIndex = idx; + } + magazineState.currentIndex = startIndex; + + // Build ordered list with category separators for nav const grouped = {}; commands.forEach(cmd => { const cat = commandCategories[cmd.id] || 'other'; @@ -66,64 +87,284 @@ function renderDesktopLayout(container, commands) { grouped[cat].push(cmd); }); - let manualHTML = ''; - categoryOrder.forEach(cat => { - if (grouped[cat] && grouped[cat].length > 0) { - manualHTML += `
${cmd.description}
+ ${flowHTML} +/${relationship.pairs}/${c}`).join(', ')}/${c}`).join(', ')}${shortDesc}
- ${relationshipHTML} -