diff --git a/content/site/partials/header.html b/content/site/partials/header.html index 0d7d796c2..d6f80c7e9 100644 --- a/content/site/partials/header.html +++ b/content/site/partials/header.html @@ -9,7 +9,7 @@ Home Docs Anti-Patterns - Live + Live Overlay diff --git a/content/site/skills/live.md b/content/site/skills/live.md index 523a237a1..99cacf47d 100644 --- a/content/site/skills/live.md +++ b/content/site/skills/live.md @@ -2,6 +2,11 @@ tagline: "Iterate on UI in the browser. Pick an element, drop a comment, get three variants. Accept one and it writes to source." --- +
+ + See it in action, with the animated demo, at /live-mode. This page is the reference for what your AI harness reads when the command runs. +
+
diff --git a/public/app.js b/public/app.js index 22beb8dd4..75f03447b 100644 --- a/public/app.js +++ b/public/app.js @@ -153,7 +153,10 @@ function renderPatternsWithTabs(patterns, antipatterns) {
`; }).join(''); - container.innerHTML = `
${tabsHTML}
${panelsHTML}`; + container.innerHTML = `
${tabsHTML}
${panelsHTML}`; + + const tabsEl = container.querySelector('.patterns-tabs'); + const tabsWrap = container.querySelector('.patterns-tabs-wrap'); container.addEventListener('click', (e) => { const tab = e.target.closest('.patterns-tab'); @@ -163,7 +166,34 @@ function renderPatternsWithTabs(patterns, antipatterns) { container.querySelectorAll('.patterns-content').forEach(p => p.classList.remove('is-active')); tab.classList.add('is-active'); container.querySelector(`.patterns-content[data-index="${index}"]`).classList.add('is-active'); + // Center the clicked tab inside the tabs strip (not the page). Using + // scrollBy on the container keeps the page scroll untouched. + if (tabsEl) { + const tabRect = tab.getBoundingClientRect(); + const stripRect = tabsEl.getBoundingClientRect(); + const offset = (tabRect.left + tabRect.width / 2) - (stripRect.left + stripRect.width / 2); + tabsEl.scrollBy({ left: offset, behavior: 'smooth' }); + } }); + + // Track scroll position so the edge-fade mask only appears on sides where + // there's actually more content. At the start, no left fade; at the end, + // no right fade; if no overflow, no fade at all. + const updateScrollState = () => { + if (!tabsEl) return; + const { scrollLeft, scrollWidth, clientWidth } = tabsEl; + const max = scrollWidth - clientWidth; + let state; + if (max <= 1) state = 'none'; + else if (scrollLeft <= 1) state = 'start'; + else if (scrollLeft >= max - 1) state = 'end'; + else state = 'middle'; + tabsEl.dataset.scroll = state; + if (tabsWrap) tabsWrap.dataset.scroll = state; + }; + tabsEl?.addEventListener('scroll', updateScrollState, { passive: true }); + window.addEventListener('resize', updateScrollState); + updateScrollState(); } // ============================================ diff --git a/public/css/docs-visuals.css b/public/css/docs-visuals.css index fe20296a8..275fb7c42 100644 --- a/public/css/docs-visuals.css +++ b/public/css/docs-visuals.css @@ -1255,3 +1255,371 @@ content: "✓"; font-weight: 700; } + +/* ============================================ + LIVE MODE LANDING PAGE (/live-mode) + + Marketing-style landing that mirrors /visual-mode's structure. + Hosts the animated homepage live-demo and surfaces the tutorial, + reference, and install pathways. + ============================================ */ + +.live-mode-page { + display: flex; + flex-direction: column; + gap: clamp(2.5rem, 5vw, 3.5rem); + max-width: 960px; + margin: 0 auto; + padding: clamp(2rem, 4vw, 3rem) clamp(1.25rem, 3vw, 2rem) clamp(4rem, 8vw, 6rem); +} + +.live-mode-page-header { + display: flex; + flex-direction: column; + gap: 10px; + max-width: 56ch; +} + +.live-mode-page-eyebrow { + display: inline-flex; + align-items: center; + gap: 10px; + font-family: var(--font-body); + font-size: 0.6875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--color-accent); +} + +.live-mode-page-eyebrow-badge { + padding: 2px 7px; + border: 1px solid var(--color-accent); + border-radius: 4px; + font-size: 0.625rem; + letter-spacing: 0.12em; +} + +.live-mode-page-title { + font-family: var(--font-display); + font-weight: 400; + font-size: clamp(2.5rem, 5vw, 3.5rem); + line-height: 1; + letter-spacing: -0.02em; + color: var(--color-ink); + margin: 0; +} + +.live-mode-page-lede { + font-family: var(--font-body); + font-size: 1.0625rem; + line-height: 1.55; + color: var(--color-charcoal); + margin: 0; + max-width: 56ch; +} + +.live-mode-start { + display: flex; + align-items: center; + gap: 10px; + margin-top: 6px; + padding: 10px 14px; + background: var(--color-ink); + color: var(--color-paper); + border-radius: 8px; + font-family: var(--font-mono); + font-size: 0.875rem; + align-self: flex-start; + max-width: 100%; +} + +.live-mode-start-prompt { + color: var(--color-accent); + user-select: none; +} + +.live-mode-start-cmd { + flex: 1; + overflow-x: auto; + white-space: nowrap; + scrollbar-width: none; +} + +.live-mode-start-cmd::-webkit-scrollbar { display: none; } + +.live-mode-start-copy { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + background: transparent; + color: oklch(75% 0 0); + border: 0; + border-radius: 4px; + cursor: pointer; + flex-shrink: 0; +} + +.live-mode-start-copy:hover { + background: oklch(22% 0 0); + color: oklch(92% 0 0); +} + +.live-mode-start-copy.is-copied { + color: oklch(70% 0.18 145); +} + +/* Demo section — wraps the animated homepage .live-demo block */ +.live-mode-demo-wrap { + margin: 0; +} + +.live-mode-demo-caption { + margin: 14px 0 0; + font-family: var(--font-body); + font-size: 0.875rem; + color: var(--color-ash); + line-height: 1.55; + font-style: italic; +} + +/* Three-card "what happens" section */ +.live-mode-stages { + display: flex; + flex-direction: column; + gap: var(--spacing-md); +} + +.live-mode-stages-title { + font-family: var(--font-display); + font-weight: 400; + font-style: italic; + font-size: 1.75rem; + color: var(--color-ink); + margin: 0 0 8px 0; +} + +.live-mode-stages-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--spacing-md); +} + +@media (max-width: 720px) { + .live-mode-stages-grid { + grid-template-columns: 1fr; + } +} + +.live-mode-stage { + display: flex; + flex-direction: column; + gap: 10px; + padding: 18px; + background: var(--color-paper); + border: 1px solid var(--color-mist); + border-radius: 8px; +} + +.live-mode-stage-num { + font-family: var(--font-mono); + font-size: 0.6875rem; + letter-spacing: 0.12em; + color: var(--color-ash); + text-transform: uppercase; +} + +.live-mode-stage-name { + font-family: var(--font-display); + font-style: italic; + font-weight: 400; + font-size: 1.375rem; + color: var(--color-ink); + margin: 0; + line-height: 1.15; +} + +.live-mode-stage-desc { + font-family: var(--font-body); + font-size: 0.875rem; + line-height: 1.55; + color: var(--color-charcoal); + margin: 0; +} + +.live-mode-stage-viz { + margin-top: 4px; + padding: 14px; + background: var(--color-cream); + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + min-height: 92px; +} + +/* Three pathway cards — tutorial, reference, install */ +.live-mode-pathways { + display: flex; + flex-direction: column; + gap: var(--spacing-md); +} + +.live-mode-pathways-title { + font-family: var(--font-display); + font-weight: 400; + font-style: italic; + font-size: 1.75rem; + color: var(--color-ink); + margin: 0 0 8px 0; +} + +.live-mode-pathways-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--spacing-md); +} + +@media (max-width: 720px) { + .live-mode-pathways-grid { + grid-template-columns: 1fr; + } +} + +.live-mode-pathway { + display: flex; + flex-direction: column; + gap: 8px; + padding: 20px; + background: var(--color-paper); + border: 1px solid var(--color-mist); + border-radius: 8px; + text-decoration: none; + transition: border-color 180ms ease, transform 180ms ease; +} + +.live-mode-pathway:hover { + border-color: var(--color-accent); +} + +.live-mode-pathway-kind { + font-family: var(--font-mono); + font-size: 0.625rem; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--color-accent); +} + +.live-mode-pathway-title { + font-family: var(--font-body); + font-size: 1.125rem; + font-weight: 600; + color: var(--color-ink); + margin: 0; + line-height: 1.3; +} + +.live-mode-pathway-desc { + font-family: var(--font-body); + font-size: 0.875rem; + color: var(--color-charcoal); + line-height: 1.55; + margin: 0; + flex: 1; +} + +.live-mode-pathway-cta { + font-family: var(--font-body); + font-size: 0.8125rem; + font-weight: 600; + color: var(--color-accent); + margin-top: 6px; +} + +/* Frameworks strip */ +.live-mode-frameworks { + display: flex; + flex-direction: column; + gap: 10px; + padding: 18px 20px; + background: var(--color-cream); + border: 1px solid var(--color-mist); + border-radius: 8px; +} + +.live-mode-frameworks-label { + font-family: var(--font-mono); + font-size: 0.6875rem; + letter-spacing: 0.12em; + color: var(--color-ash); + text-transform: uppercase; +} + +.live-mode-frameworks-list { + display: flex; + flex-wrap: wrap; + gap: 8px 14px; + font-family: var(--font-body); + font-size: 0.875rem; + color: var(--color-ink); + margin: 0; + padding: 0; + list-style: none; +} + +.live-mode-frameworks-list li { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.live-mode-frameworks-list li::before { + content: ""; + display: inline-block; + width: 5px; + height: 5px; + border-radius: 50%; + background: var(--color-accent); +} + +/* Docs/live page callout pointing back to /live-mode */ +.docs-live-callout { + display: flex; + align-items: center; + gap: 14px; + padding: 12px 16px; + margin: 0 0 clamp(1.25rem, 2.5vw, 1.75rem); + background: var(--color-accent-dim); + border: 1px solid var(--color-accent-soft); + border-radius: 8px; + font-family: var(--font-body); + font-size: 0.875rem; + color: var(--color-ink); + line-height: 1.5; +} + +.docs-live-callout-icon { + flex-shrink: 0; + width: 28px; + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + background: var(--color-accent); + color: var(--color-paper); + border-radius: 50%; + font-size: 14px; +} + +.docs-live-callout-text { + flex: 1; +} + +.docs-live-callout-text a { + color: var(--color-accent); + text-decoration: underline; + text-underline-offset: 2px; + font-weight: 600; +} diff --git a/public/css/live-mode.css b/public/css/live-mode.css new file mode 100644 index 000000000..c15c484d2 --- /dev/null +++ b/public/css/live-mode.css @@ -0,0 +1,802 @@ +/* ============================================ + LIVE MODE — interactive demo loop + ============================================ */ + +.live-section { + padding: var(--spacing-2xl) 0; + border-top: 1px solid var(--color-mist); +} + +.live-content .section-lead { + max-width: 64ch; + margin-bottom: var(--spacing-xl); +} + +.live-demo { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(260px, 340px); + gap: var(--spacing-xl); + align-items: start; + margin-bottom: var(--spacing-xl); +} +.live-demo-frame-col { + display: flex; + flex-direction: column; + gap: var(--spacing-md); + min-width: 0; +} +@media (max-width: 920px) { + .live-demo { grid-template-columns: 1fr; } +} + +.live-demo-frame { + position: relative; + background: var(--color-cream); + border: 1px solid var(--color-mist); + border-radius: 12px; + overflow: hidden; + aspect-ratio: 16 / 9; + max-width: 960px; + box-shadow: 0 20px 50px oklch(0% 0 0 / 0.08); + user-select: none; +} + +.live-demo-chrome { + display: flex; + align-items: center; + gap: 7px; + padding: 10px 14px; + border-bottom: 1px solid var(--color-mist); + background: var(--color-paper); +} +.live-demo-dot { + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--color-mist); +} +.live-demo-url { + margin-left: 14px; + padding: 4px 14px; + background: var(--color-cream); + border: 1px solid var(--color-mist); + border-radius: 5px; + font-family: var(--font-mono); + font-size: 11px; + color: var(--color-charcoal); +} + +.live-demo-stage { + position: relative; + height: calc(100% - 42px - 60px); /* minus chrome + bar */ + display: flex; + align-items: center; + justify-content: center; + padding: 32px; + overflow: hidden; +} + +/* Target card */ +.live-demo-target { + position: relative; + width: min(360px, 80%); + min-height: 200px; +} +.live-demo-variant { + position: absolute; + inset: 0; + opacity: 0; + transform: translateY(8px) scale(0.99); + transition: opacity 280ms var(--ease-out), transform 280ms var(--ease-out); + pointer-events: none; +} +.live-demo-variant.is-active { + opacity: 1; + transform: translateY(0) scale(1); + pointer-events: auto; +} +.live-demo-card { + display: flex; + flex-direction: column; + gap: 10px; + padding: 22px 24px; + background: var(--color-paper); + border: 1px solid var(--color-mist); + border-radius: 8px; + height: 100%; + box-sizing: border-box; +} +.live-demo-card-kicker { + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--color-ash); +} +.live-demo-card h3 { + font-family: var(--font-display); + font-weight: 400; + font-size: 20px; + line-height: 1.2; + color: var(--color-ink); + margin: 0; +} +.live-demo-card p { + font-family: var(--font-body); + font-size: 13px; + line-height: 1.5; + color: var(--color-charcoal); + margin: 0; +} +.live-demo-card button { + align-self: flex-start; + margin-top: auto; + font-family: var(--font-body); + font-weight: 500; + font-size: 12px; + letter-spacing: 0.06em; + text-transform: uppercase; + padding: 9px 16px; + background: var(--color-ink); + color: var(--color-paper); + border: 0; + border-radius: 0; + cursor: pointer; +} + +/* Per-variant look */ +.live-demo-card--plain {} +.live-demo-card--v1 { + background: var(--color-cream); +} +.live-demo-card--v1 h3 { font-style: italic; } +.live-demo-card--v1 h3 em { color: var(--color-accent); font-style: italic; } +.live-demo-card--v2 { + background: var(--color-paper); + border: 1px dashed var(--color-accent); +} +.live-demo-card--v2 .live-demo-card-kicker { color: var(--color-accent); } +.live-demo-card--v2 h3 { font-family: var(--font-display); font-style: italic; } +.live-demo-card--v2 button { + background: var(--color-accent); +} +.live-demo-card--v3 { + background: oklch(96% 0.02 350); + border: 1px solid var(--color-accent-soft); +} +.live-demo-card--v3 h3 em { color: var(--color-accent); font-style: italic; } +.live-demo-card--v3 .live-demo-card-kicker { color: var(--color-accent-hover); } + +/* Highlight outline over the target */ +.live-demo-outline { + position: absolute; + border: 2px solid var(--color-accent); + border-radius: 8px; + pointer-events: none; + opacity: 0; + transition: opacity 200ms var(--ease-out), top 320ms var(--ease-out), left 320ms var(--ease-out), width 320ms var(--ease-out), height 320ms var(--ease-out); + box-shadow: 0 0 0 4px var(--color-accent-dim); +} +.live-demo-outline.is-visible { + opacity: 1; +} + +/* Annotations */ +.live-demo-annotations { + position: absolute; + inset: 0; + pointer-events: none; + opacity: 0; + transition: opacity 280ms var(--ease-out); + color: var(--color-accent); +} +.live-demo-annotations.is-visible { opacity: 1; } +.live-demo-stroke { + position: absolute; + width: 280px; + height: 56px; + top: 50%; + left: 50%; + transform: translate(-50%, -24px); + pointer-events: none; +} +.live-demo-stroke path { + stroke-dasharray: 1; + stroke-dashoffset: 1; +} +.live-demo-annotations.is-visible .live-demo-stroke path { + animation: liveDemoStroke 800ms var(--ease-out) forwards; +} +@keyframes liveDemoStroke { + to { stroke-dashoffset: 0; } +} +.live-demo-comment { + position: absolute; + top: 56%; + left: 50%; + transform: translate(-50%, 0); + padding: 5px 10px; + background: var(--color-ink); + color: var(--color-paper); + font-family: var(--font-mono); + font-size: 11px; + border-radius: 4px; + white-space: nowrap; + opacity: 0; + transition: opacity 200ms var(--ease-out); +} +.live-demo-annotations.is-comment-visible .live-demo-comment { opacity: 1; } + +/* Simulated cursor */ +.live-demo-cursor { + position: absolute; + top: 0; + left: 0; + width: 18px; + height: 22px; + pointer-events: none; + opacity: 0; + transform: translate(0, 0); + transition: opacity 200ms var(--ease-out), transform 560ms var(--ease-out-quint); + z-index: 5; + filter: drop-shadow(0 2px 4px oklch(0% 0 0 / 0.2)); +} +.live-demo-cursor.is-visible { opacity: 1; } +.live-demo-cursor.is-click svg path { + transform-origin: 4px 4px; + animation: liveDemoCursorClick 220ms var(--ease-out); +} +@keyframes liveDemoCursorClick { + 0%, 100% { transform: scale(1); } + 40% { transform: scale(0.78); } +} + +/* Global bar — the persistent dark pill at the bottom of every session. */ +.live-demo-gbar { + position: absolute; + bottom: 14px; + left: 50%; + transform: translateX(-50%); + min-height: 36px; + padding: 0 4px; + background: oklch(14% 0 0); + color: oklch(92% 0 0); + border: 1px solid oklch(22% 0 0); + border-radius: 10px; + display: flex; + align-items: center; + gap: 2px; + font-family: var(--font-body); + font-size: 12px; + box-shadow: 0 8px 24px oklch(0% 0 0 / 0.2); + z-index: 4; +} +.live-demo-gbar-brand { + font-family: var(--font-display); + font-size: 16px; + color: var(--color-accent); + padding: 0 10px; +} +.live-demo-gbar-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 7px 10px; + background: transparent; + border: 0; + border-radius: 7px; + color: oklch(75% 0 0); + font: inherit; + cursor: pointer; +} +.live-demo-gbar-btn.is-active { + background: var(--color-accent-dim); + color: var(--color-accent); +} +.live-demo-gbar-dmd { + display: inline-grid; + grid-template: repeat(2, 1fr) / repeat(2, 1fr); + width: 12px; + height: 12px; + border-radius: 3px; + overflow: hidden; +} +.live-demo-gbar-dmd span:nth-child(1) { background: oklch(60% 0.25 350); } +.live-demo-gbar-dmd span:nth-child(2) { background: oklch(60% 0.15 45); } +.live-demo-gbar-dmd span:nth-child(3) { background: oklch(55% 0.12 250); } +.live-demo-gbar-dmd span:nth-child(4) { background: oklch(30% 0 0); } +.live-demo-gbar-divider { + width: 1px; + height: 18px; + background: oklch(28% 0 0); + margin: 0 4px; +} +.live-demo-gbar-x { + padding: 7px 10px; + background: transparent; + border: 0; + border-radius: 7px; + color: oklch(60% 0 0); + font-size: 13px; + cursor: pointer; +} + +/* Contextual bar — LIGHT paper-backed pill that floats near the picked + element during a session. Matches live-browser.js's buildConfigureRow + look (dark command pill, transparent input, ×N count, magenta Go). */ +.live-demo-ctx { + position: absolute; + left: 50%; + transform: translate(-50%, 6px); + padding: 6px; + background: var(--color-paper); + border: 1px solid var(--color-mist); + border-radius: 10px; + box-shadow: 0 4px 20px oklch(0% 0 0 / 0.08), 0 1px 3px oklch(0% 0 0 / 0.06); + font-family: var(--font-body); + font-size: 12px; + color: var(--color-ink); + display: none; + opacity: 0; + transition: opacity 260ms var(--ease-out), transform 260ms var(--ease-out); + z-index: 3; + max-width: 90%; +} +.live-demo-ctx[data-phase="configuring"], +.live-demo-ctx[data-phase="generating"], +.live-demo-ctx[data-phase="cycling"], +.live-demo-ctx[data-phase="accepted"] { + display: block; + opacity: 1; + transform: translate(-50%, 0); +} +.live-demo-ctx-row { + display: none; + align-items: center; + gap: 4px; +} +.live-demo-ctx[data-phase="configuring"] .live-demo-ctx-row--configure, +.live-demo-ctx[data-phase="generating"] .live-demo-ctx-row--generating, +.live-demo-ctx[data-phase="cycling"] .live-demo-ctx-row--cycling, +.live-demo-ctx[data-phase="accepted"] .live-demo-ctx-row--accepted { + display: flex; +} + +/* Configure row: [delight ▾] [input] [×3] [Go →] */ +.live-demo-ctx-pill { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 5px 10px; + background: var(--color-ink); + color: var(--color-paper); + border: 0; + border-radius: 6px; + font-family: var(--font-body); + font-size: 12px; + font-weight: 500; + cursor: pointer; + white-space: nowrap; +} +.live-demo-ctx-pill-caret { font-size: 9px; opacity: 0.7; margin-left: 2px; } +.live-demo-ctx-input { + display: inline-flex; + align-items: center; + flex: 1; + min-width: 180px; + padding: 5px 8px; + font-family: var(--font-body); + font-size: 12px; + color: var(--color-ink); +} +.live-demo-ctx-caret { + display: inline-block; + width: 1px; + height: 13px; + background: var(--color-ink); + margin-left: 2px; + animation: liveDemoCaret 1s steps(1) infinite; +} +@keyframes liveDemoCaret { + 50% { opacity: 0; } +} +.live-demo-ctx-count { + padding: 4px 6px; + background: transparent; + border: 1px solid var(--color-mist); + border-radius: 5px; + font-family: var(--font-mono); + font-size: 11px; + font-weight: 600; + color: var(--color-ash); + cursor: pointer; +} +.live-demo-ctx-go { + padding: 5px 12px; + background: var(--color-accent); + color: var(--color-paper); + border: 0; + border-radius: 6px; + font-family: var(--font-body); + font-size: 12px; + font-weight: 600; + cursor: pointer; + white-space: nowrap; +} + +/* Generating row */ +.live-demo-ctx-row--generating { + gap: 10px; + padding: 4px 12px 4px 6px; + font-family: var(--font-body); + font-size: 12px; + color: var(--color-charcoal); +} +.live-demo-ctx-spinner { + width: 14px; + height: 14px; + border-radius: 50%; + border: 1.5px solid var(--color-mist); + border-top-color: var(--color-accent); + animation: liveDemoSpin 700ms linear infinite; +} +@keyframes liveDemoSpin { to { transform: rotate(360deg); } } + +/* Cycling row */ +.live-demo-ctx-row--cycling { gap: 2px; padding: 2px; } +.live-demo-ctx-nav { + padding: 4px 10px; + background: transparent; + border: 0; + border-radius: 5px; + color: var(--color-charcoal); + font-size: 14px; + cursor: pointer; +} +.live-demo-ctx-counter { + font-family: var(--font-mono); + font-size: 11px; + color: var(--color-ink); + padding: 0 6px; + min-width: 40px; + text-align: center; +} +.live-demo-ctx-divider { + width: 1px; + height: 18px; + background: var(--color-mist); + margin: 0 4px; +} +.live-demo-ctx-discard { + padding: 4px 10px; + background: transparent; + border: 0; + border-radius: 5px; + color: var(--color-ash); + font-size: 13px; + cursor: pointer; +} +.live-demo-ctx-accept { + padding: 5px 14px; + background: var(--color-ink); + color: var(--color-paper); + border: 0; + border-radius: 6px; + font-family: var(--font-body); + font-size: 12px; + font-weight: 600; + cursor: pointer; +} + +/* Accepted confirmation row */ +.live-demo-ctx-row--accepted { + gap: 8px; + padding: 6px 14px; + font-family: var(--font-body); + font-size: 12px; + color: oklch(45% 0.18 145); +} + +/* Phase caption under the frame */ +.live-demo-caption { + display: flex; + align-items: baseline; + gap: 10px; + font-family: var(--font-mono); + font-size: 11px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--color-ash); +} +.live-demo-caption::before { + content: ""; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--color-accent); + animation: liveDemoPulse 1.6s var(--ease-out) infinite; +} +@keyframes liveDemoPulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.4; transform: scale(0.7); } +} +.live-demo-caption-label { color: var(--color-ink); } + +/* Supporting row under the demo */ +.live-demo-support { + display: flex; + flex-direction: column; + gap: var(--spacing-md); + align-self: stretch; +} +.live-demo-support-cell { + display: grid; + grid-template-columns: 1fr; + gap: 4px; + padding-bottom: var(--spacing-md); + border-bottom: 1px dashed var(--color-mist); +} +.live-demo-support-cell:last-child { + border-bottom: 0; + padding-bottom: 0; +} +.live-demo-support-k { + font-family: var(--font-mono); + font-size: 12px; + font-weight: 600; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--color-accent); +} +.live-demo-support-v { + font-family: var(--font-body); + font-size: 14px; + line-height: 1.55; + color: var(--color-ink); +} +.live-demo-support-v code { + font-family: var(--font-mono); + font-size: 0.875em; + background: var(--color-mist); + padding: 1px 6px; + border-radius: 3px; + color: var(--color-ink); +} + +@media (max-width: 760px) { + .live-demo-frame { aspect-ratio: 4 / 5; } + .live-demo-target { width: 90%; } + .live-demo-bar { font-size: 11px; min-height: 32px; } + .live-demo-support { grid-template-columns: 1fr; gap: var(--spacing-md); } +} + +@media (prefers-reduced-motion: reduce) { + .live-demo-cursor, + .live-demo-outline, + .live-demo-variant, + .live-demo-annotations, + .live-demo-stroke path { + transition: none !important; + animation: none !important; + } + .live-demo-caption::before { animation: none; } +} +/* ============================================ + LIVE MODE — skeleton page + richer variants + ============================================ */ + +/* Skeleton elements behind the target card — suggest "this is a real page" */ +.live-demo-skeleton { + position: absolute; + inset: 24px; + display: flex; + flex-direction: column; + gap: 14px; + opacity: 0.3; + pointer-events: none; + filter: blur(0.3px); +} +.live-demo-skel-nav { + display: flex; + align-items: center; + gap: 10px; + padding-bottom: 12px; + border-bottom: 1px solid var(--color-mist); +} +.live-demo-skel-logo { + width: 22px; + height: 22px; + border-radius: 4px; + background: var(--color-charcoal); +} +.live-demo-skel-link { + width: 48px; + height: 8px; + border-radius: 2px; + background: var(--color-mist); +} +.live-demo-skel-cta { + margin-left: auto; + width: 72px; + height: 24px; + border-radius: 4px; + background: var(--color-charcoal); +} +.live-demo-skel-heading { + width: 60%; + height: 18px; + border-radius: 3px; + background: var(--color-mist); + margin-top: 20px; +} +.live-demo-skel-line { + height: 8px; + border-radius: 2px; + background: var(--color-mist); +} +.live-demo-skel-line--short { width: 40%; } + +/* Target stays above skeleton */ +.live-demo-target { position: relative; z-index: 1; } + +/* ---- Card variants — sharper, more distinct ---- */ + +/* Variant 1: magazine column — italic serif, rule lines, off-white */ +.live-demo-card--v1 { + background: var(--color-cream); + padding: 18px 22px; + border: 0; + border-top: 3px solid var(--color-ink); + border-radius: 0; + gap: 6px; +} +.live-demo-card--v1 .live-demo-card-kicker { + color: var(--color-accent); + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.28em; +} +.live-demo-card--v1 h3 { + font-family: var(--font-display); + font-style: italic; + font-weight: 400; + font-size: 28px; + line-height: 1.05; + letter-spacing: -0.01em; + margin-top: 4px; +} +.live-demo-card--v1 h3 em { color: var(--color-accent); font-style: italic; } +.live-demo-card--v1 p { + font-family: var(--font-display); + font-style: italic; + font-size: 14px; + line-height: 1.55; + color: var(--color-charcoal); +} +.live-demo-card--v1 button { + font-family: var(--font-body); + align-self: flex-start; + margin-top: 6px; + background: transparent; + color: var(--color-ink); + border: 0; + border-bottom: 1.5px solid var(--color-ink); + padding: 4px 0; + border-radius: 0; + letter-spacing: 0.08em; +} + +/* Variant 2: brutalist ticket — thick ink block, stamp glyph, mono */ +.live-demo-card--v2 { + position: relative; + background: var(--color-ink); + color: var(--color-paper); + padding: 20px 24px; + border: 0; + border-radius: 0; + overflow: hidden; + gap: 8px; +} +.live-demo-card--v2::before { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 6px; + background: var(--color-accent); +} +.live-demo-card-stamp { + position: absolute; + top: 14px; + right: 18px; + font-size: 28px; + line-height: 1; + color: var(--color-accent); + transform: rotate(-8deg); +} +.live-demo-card--v2 .live-demo-card-kicker { + color: var(--color-accent); + letter-spacing: 0.3em; + font-weight: 600; +} +.live-demo-card--v2 h3 { + font-family: var(--font-body); + font-weight: 700; + font-size: 24px; + line-height: 1.1; + color: var(--color-paper); + letter-spacing: -0.02em; +} +.live-demo-card--v2 button { + font-family: var(--font-mono); + font-weight: 500; + align-self: flex-start; + margin-top: 6px; + background: var(--color-accent); + color: var(--color-paper); + border: 0; + border-radius: 0; + padding: 9px 14px; + letter-spacing: 0.04em; + text-transform: none; + font-size: 12px; +} + +/* Variant 3: playful ticket — accent background, sticker stars, postcard vibe */ +.live-demo-card--v3 { + position: relative; + background: + radial-gradient(circle at 20% 80%, oklch(92% 0.08 350) 0, transparent 45%), + var(--color-cream); + border: 1px dashed var(--color-accent); + border-radius: 10px; + padding: 22px 24px 20px; + gap: 8px; +} +.live-demo-card-sticker { + position: absolute; + top: 10px; + right: 14px; + display: flex; + gap: 3px; + color: var(--color-accent); + font-size: 14px; + line-height: 1; + transform: rotate(6deg); +} +.live-demo-card--v3 .live-demo-card-kicker { + font-family: var(--font-display); + font-style: italic; + font-weight: 400; + font-size: 13px; + letter-spacing: 0.04em; + color: var(--color-accent-hover); + text-transform: none; +} +.live-demo-card--v3 h3 { + font-family: var(--font-display); + font-weight: 400; + font-size: 22px; + line-height: 1.2; + color: var(--color-ink); +} +.live-demo-card--v3 button { + display: inline-flex; + align-items: center; + gap: 6px; + align-self: flex-start; + background: var(--color-ink); + color: var(--color-paper); + border: 0; + border-radius: 999px; + padding: 10px 18px; + font-family: var(--font-body); + font-weight: 500; + letter-spacing: 0.06em; + text-transform: none; + font-size: 12px; + margin-top: 6px; +} + diff --git a/public/css/main.css b/public/css/main.css index 43dfa0fc0..5f90f300d 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -11,6 +11,7 @@ /* Section partials (landing page only) */ @import "./problem-section.css"; @import "./workflow.css"; +@import "./live-mode.css"; @import "./gallery.css"; @import "./skill-demos.css"; @@ -1153,7 +1154,7 @@ code { .language-content { display: grid; - gap: var(--spacing-lg); + gap: var(--spacing-sm); } /* Grid and flex items default to min-width: auto, which lets them expand to @@ -2442,28 +2443,76 @@ code { padding: var(--spacing-lg); } -.patterns-tabs { - display: flex; - flex-wrap: wrap; - gap: 6px; +.patterns-tabs-wrap { + position: relative; margin-bottom: var(--spacing-md); } -@media (max-width: 768px) { - .patterns-tabs { - flex-wrap: nowrap; - overflow-x: auto; - scrollbar-width: none; - -webkit-overflow-scrolling: touch; - margin-left: calc(-1 * var(--spacing-lg)); - margin-right: calc(-1 * var(--spacing-lg)); - padding-left: var(--spacing-lg); - padding-right: var(--spacing-lg); - } +.patterns-tabs { + display: flex; + flex-wrap: nowrap; + gap: 6px; + overflow-x: auto; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; + scroll-snap-type: x proximity; +} - .patterns-tabs::-webkit-scrollbar { - display: none; - } +.patterns-tabs::-webkit-scrollbar { + display: none; +} + +/* Edge fade only on sides where more content exists. data-scroll is set + by JS: start | middle | end | none. */ +.patterns-tabs[data-scroll="start"] { + mask-image: linear-gradient(to right, black calc(100% - 40px), transparent); + -webkit-mask-image: linear-gradient(to right, black calc(100% - 40px), transparent); +} +.patterns-tabs[data-scroll="middle"] { + mask-image: linear-gradient(to right, transparent, black 32px, black calc(100% - 40px), transparent); + -webkit-mask-image: linear-gradient(to right, transparent, black 32px, black calc(100% - 40px), transparent); +} +.patterns-tabs[data-scroll="end"] { + mask-image: linear-gradient(to right, transparent, black 32px); + -webkit-mask-image: linear-gradient(to right, transparent, black 32px); +} +.patterns-tabs[data-scroll="none"] { + mask-image: none; + -webkit-mask-image: none; +} + +/* Chevrons sit on the wrapper (outside the mask) so they're always fully + opaque, regardless of edge fade. Visible only when scroll can progress + in that direction. */ +.patterns-tabs-wrap::before, +.patterns-tabs-wrap::after { + content: ''; + position: absolute; + top: 50%; + width: 9px; + height: 9px; + border-top: 1.5px solid var(--color-ash); + border-right: 1.5px solid var(--color-ash); + pointer-events: none; + opacity: 0; + transition: opacity 0.18s ease; +} +.patterns-tabs-wrap::before { + left: 2px; + transform: translateY(-50%) rotate(-135deg); +} +.patterns-tabs-wrap::after { + right: 2px; + transform: translateY(-50%) rotate(45deg); +} +.patterns-tabs-wrap[data-scroll="start"]::after, +.patterns-tabs-wrap[data-scroll="middle"]::after { opacity: 1; } +.patterns-tabs-wrap[data-scroll="end"]::before, +.patterns-tabs-wrap[data-scroll="middle"]::before { opacity: 1; } + +.patterns-tab { + scroll-snap-align: start; + flex-shrink: 0; } .patterns-tab { diff --git a/public/css/workflow.css b/public/css/workflow.css index 6c60b4843..1c7905360 100644 --- a/public/css/workflow.css +++ b/public/css/workflow.css @@ -1472,11 +1472,27 @@ border-radius: 10px; padding: var(--spacing-xl); box-shadow: 0 1px 2px oklch(0.2 0 0 / 0.04); + /* All panels stack in one grid cell so the outgoing panel can cross-fade + out while the incoming one fades in. Container auto-sizes to the + tallest panel, so switching between tabs doesn't jump. */ + display: grid; + grid-template-areas: "stack"; } -.why-panel { - display: none; - animation: whyFadeIn 260ms var(--ease-out); +.why-panel, +.why-panel[hidden] { + /* [hidden] attribute on initial markup would force display:none and skip + the crossfade; keep the element rendered so opacity/transform can run. */ + display: block; + grid-area: stack; + opacity: 0; + visibility: hidden; + transform: translateY(8px); + transition: + opacity 650ms var(--ease-out), + transform 800ms var(--ease-out), + visibility 0s linear 650ms; + pointer-events: none; } .why-panel-title, @@ -1486,7 +1502,14 @@ } .why-panel.is-active { - display: block; + opacity: 1; + visibility: visible; + transform: translateY(0); + pointer-events: auto; + transition: + opacity 650ms var(--ease-out), + transform 800ms var(--ease-out), + visibility 0s linear 0s; } .why-panel-title { @@ -1557,13 +1580,12 @@ .why-panel-meta a:hover { color: var(--color-accent); } -@keyframes whyFadeIn { - from { opacity: 0; transform: translateY(6px); } - to { opacity: 1; transform: translateY(0); } -} - @media (prefers-reduced-motion: reduce) { - .why-panel { animation: none; } + .why-panel, + .why-panel.is-active { + transition: none; + transform: none; + } } @media (max-width: 900px) { @@ -1613,7 +1635,7 @@ } .why-tab.is-active.is-cycling .why-tab-progress { - animation: whyTabProgress var(--why-cycle-ms, 7000ms) cubic-bezier(0.4, 0, 0.2, 1) forwards; + animation: whyTabProgress var(--why-cycle-ms, 7000ms) linear forwards; } @keyframes whyTabProgress { @@ -2182,12 +2204,50 @@ /* ─ Panel 01: PRODUCT.md file view ─ */ .why-visual--productmd { padding: 0; - background: var(--color-paper); + background: transparent; + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: var(--spacing-lg); + align-items: start; +} +@media (max-width: 780px) { + .why-visual--productmd { + grid-template-columns: 1fr; + } } .why-productmd-file { - flex: 1; display: flex; flex-direction: column; + background: var(--color-paper); + border: 1px solid var(--color-mist); + border-radius: 8px; + overflow: hidden; + min-width: 0; +} +.why-productmd-commands { + display: flex; + flex-direction: column; + gap: 8px; + padding-top: 4px; + min-width: 160px; +} +.why-productmd-commands-label { + font-family: var(--font-mono); + font-size: 9.5px; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--color-ash); + margin-bottom: 4px; +} +.why-productmd-commands code { + font-family: var(--font-mono); + font-size: 12px; + color: var(--color-ink); + background: var(--color-paper); + border: 1px solid var(--color-mist); + border-radius: 5px; + padding: 5px 9px; + white-space: nowrap; } .why-productmd-header { display: flex; @@ -2701,590 +2761,6 @@ margin-top: -2px; } -/* ============================================ - LIVE MODE — interactive demo loop - ============================================ */ - -.live-section { - padding: var(--spacing-2xl) 0; - border-top: 1px solid var(--color-mist); -} - -.live-content .section-lead { - max-width: 64ch; - margin-bottom: var(--spacing-xl); -} - -.live-demo { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(260px, 340px); - gap: var(--spacing-xl); - align-items: start; - margin-bottom: var(--spacing-xl); -} -.live-demo-frame-col { - display: flex; - flex-direction: column; - gap: var(--spacing-md); - min-width: 0; -} -@media (max-width: 920px) { - .live-demo { grid-template-columns: 1fr; } -} - -.live-demo-frame { - position: relative; - background: var(--color-cream); - border: 1px solid var(--color-mist); - border-radius: 12px; - overflow: hidden; - aspect-ratio: 16 / 9; - max-width: 960px; - box-shadow: 0 20px 50px oklch(0% 0 0 / 0.08); - user-select: none; -} - -.live-demo-chrome { - display: flex; - align-items: center; - gap: 7px; - padding: 10px 14px; - border-bottom: 1px solid var(--color-mist); - background: var(--color-paper); -} -.live-demo-dot { - width: 10px; - height: 10px; - border-radius: 50%; - background: var(--color-mist); -} -.live-demo-url { - margin-left: 14px; - padding: 4px 14px; - background: var(--color-cream); - border: 1px solid var(--color-mist); - border-radius: 5px; - font-family: var(--font-mono); - font-size: 11px; - color: var(--color-charcoal); -} - -.live-demo-stage { - position: relative; - height: calc(100% - 42px - 60px); /* minus chrome + bar */ - display: flex; - align-items: center; - justify-content: center; - padding: 32px; - overflow: hidden; -} - -/* Target card */ -.live-demo-target { - position: relative; - width: min(360px, 80%); - min-height: 200px; -} -.live-demo-variant { - position: absolute; - inset: 0; - opacity: 0; - transform: translateY(8px) scale(0.99); - transition: opacity 280ms var(--ease-out), transform 280ms var(--ease-out); - pointer-events: none; -} -.live-demo-variant.is-active { - opacity: 1; - transform: translateY(0) scale(1); - pointer-events: auto; -} -.live-demo-card { - display: flex; - flex-direction: column; - gap: 10px; - padding: 22px 24px; - background: var(--color-paper); - border: 1px solid var(--color-mist); - border-radius: 8px; - height: 100%; - box-sizing: border-box; -} -.live-demo-card-kicker { - font-family: var(--font-mono); - font-size: 10px; - letter-spacing: 0.18em; - text-transform: uppercase; - color: var(--color-ash); -} -.live-demo-card h3 { - font-family: var(--font-display); - font-weight: 400; - font-size: 20px; - line-height: 1.2; - color: var(--color-ink); - margin: 0; -} -.live-demo-card p { - font-family: var(--font-body); - font-size: 13px; - line-height: 1.5; - color: var(--color-charcoal); - margin: 0; -} -.live-demo-card button { - align-self: flex-start; - margin-top: auto; - font-family: var(--font-body); - font-weight: 500; - font-size: 12px; - letter-spacing: 0.06em; - text-transform: uppercase; - padding: 9px 16px; - background: var(--color-ink); - color: var(--color-paper); - border: 0; - border-radius: 0; - cursor: pointer; -} - -/* Per-variant look */ -.live-demo-card--plain {} -.live-demo-card--v1 { - background: var(--color-cream); -} -.live-demo-card--v1 h3 { font-style: italic; } -.live-demo-card--v1 h3 em { color: var(--color-accent); font-style: italic; } -.live-demo-card--v2 { - background: var(--color-paper); - border: 1px dashed var(--color-accent); -} -.live-demo-card--v2 .live-demo-card-kicker { color: var(--color-accent); } -.live-demo-card--v2 h3 { font-family: var(--font-display); font-style: italic; } -.live-demo-card--v2 button { - background: var(--color-accent); -} -.live-demo-card--v3 { - background: oklch(96% 0.02 350); - border: 1px solid var(--color-accent-soft); -} -.live-demo-card--v3 h3 em { color: var(--color-accent); font-style: italic; } -.live-demo-card--v3 .live-demo-card-kicker { color: var(--color-accent-hover); } - -/* Highlight outline over the target */ -.live-demo-outline { - position: absolute; - border: 2px solid var(--color-accent); - border-radius: 8px; - pointer-events: none; - opacity: 0; - transition: opacity 200ms var(--ease-out), top 320ms var(--ease-out), left 320ms var(--ease-out), width 320ms var(--ease-out), height 320ms var(--ease-out); - box-shadow: 0 0 0 4px var(--color-accent-dim); -} -.live-demo-outline.is-visible { - opacity: 1; -} - -/* Annotations */ -.live-demo-annotations { - position: absolute; - inset: 0; - pointer-events: none; - opacity: 0; - transition: opacity 280ms var(--ease-out); - color: var(--color-accent); -} -.live-demo-annotations.is-visible { opacity: 1; } -.live-demo-stroke { - position: absolute; - width: 280px; - height: 56px; - top: 50%; - left: 50%; - transform: translate(-50%, -24px); - pointer-events: none; -} -.live-demo-stroke path { - stroke-dasharray: 1; - stroke-dashoffset: 1; -} -.live-demo-annotations.is-visible .live-demo-stroke path { - animation: liveDemoStroke 800ms var(--ease-out) forwards; -} -@keyframes liveDemoStroke { - to { stroke-dashoffset: 0; } -} -.live-demo-comment { - position: absolute; - top: 56%; - left: 50%; - transform: translate(-50%, 0); - padding: 5px 10px; - background: var(--color-ink); - color: var(--color-paper); - font-family: var(--font-mono); - font-size: 11px; - border-radius: 4px; - white-space: nowrap; - opacity: 0; - transition: opacity 200ms var(--ease-out); -} -.live-demo-annotations.is-comment-visible .live-demo-comment { opacity: 1; } - -/* Simulated cursor */ -.live-demo-cursor { - position: absolute; - top: 0; - left: 0; - width: 18px; - height: 22px; - pointer-events: none; - opacity: 0; - transform: translate(0, 0); - transition: opacity 200ms var(--ease-out), transform 560ms var(--ease-out-quint); - z-index: 5; - filter: drop-shadow(0 2px 4px oklch(0% 0 0 / 0.2)); -} -.live-demo-cursor.is-visible { opacity: 1; } -.live-demo-cursor.is-click svg path { - transform-origin: 4px 4px; - animation: liveDemoCursorClick 220ms var(--ease-out); -} -@keyframes liveDemoCursorClick { - 0%, 100% { transform: scale(1); } - 40% { transform: scale(0.78); } -} - -/* Global bar — the persistent dark pill at the bottom of every session. */ -.live-demo-gbar { - position: absolute; - bottom: 14px; - left: 50%; - transform: translateX(-50%); - min-height: 36px; - padding: 0 4px; - background: oklch(14% 0 0); - color: oklch(92% 0 0); - border: 1px solid oklch(22% 0 0); - border-radius: 10px; - display: flex; - align-items: center; - gap: 2px; - font-family: var(--font-body); - font-size: 12px; - box-shadow: 0 8px 24px oklch(0% 0 0 / 0.2); - z-index: 4; -} -.live-demo-gbar-brand { - font-family: var(--font-display); - font-size: 16px; - color: var(--color-accent); - padding: 0 10px; -} -.live-demo-gbar-btn { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 7px 10px; - background: transparent; - border: 0; - border-radius: 7px; - color: oklch(75% 0 0); - font: inherit; - cursor: pointer; -} -.live-demo-gbar-btn.is-active { - background: var(--color-accent-dim); - color: var(--color-accent); -} -.live-demo-gbar-dmd { - display: inline-grid; - grid-template: repeat(2, 1fr) / repeat(2, 1fr); - width: 12px; - height: 12px; - border-radius: 3px; - overflow: hidden; -} -.live-demo-gbar-dmd span:nth-child(1) { background: oklch(60% 0.25 350); } -.live-demo-gbar-dmd span:nth-child(2) { background: oklch(60% 0.15 45); } -.live-demo-gbar-dmd span:nth-child(3) { background: oklch(55% 0.12 250); } -.live-demo-gbar-dmd span:nth-child(4) { background: oklch(30% 0 0); } -.live-demo-gbar-divider { - width: 1px; - height: 18px; - background: oklch(28% 0 0); - margin: 0 4px; -} -.live-demo-gbar-x { - padding: 7px 10px; - background: transparent; - border: 0; - border-radius: 7px; - color: oklch(60% 0 0); - font-size: 13px; - cursor: pointer; -} - -/* Contextual bar — LIGHT paper-backed pill that floats near the picked - element during a session. Matches live-browser.js's buildConfigureRow - look (dark command pill, transparent input, ×N count, magenta Go). */ -.live-demo-ctx { - position: absolute; - left: 50%; - transform: translate(-50%, 6px); - padding: 6px; - background: var(--color-paper); - border: 1px solid var(--color-mist); - border-radius: 10px; - box-shadow: 0 4px 20px oklch(0% 0 0 / 0.08), 0 1px 3px oklch(0% 0 0 / 0.06); - font-family: var(--font-body); - font-size: 12px; - color: var(--color-ink); - display: none; - opacity: 0; - transition: opacity 260ms var(--ease-out), transform 260ms var(--ease-out); - z-index: 3; - max-width: 90%; -} -.live-demo-ctx[data-phase="configuring"], -.live-demo-ctx[data-phase="generating"], -.live-demo-ctx[data-phase="cycling"], -.live-demo-ctx[data-phase="accepted"] { - display: block; - opacity: 1; - transform: translate(-50%, 0); -} -.live-demo-ctx-row { - display: none; - align-items: center; - gap: 4px; -} -.live-demo-ctx[data-phase="configuring"] .live-demo-ctx-row--configure, -.live-demo-ctx[data-phase="generating"] .live-demo-ctx-row--generating, -.live-demo-ctx[data-phase="cycling"] .live-demo-ctx-row--cycling, -.live-demo-ctx[data-phase="accepted"] .live-demo-ctx-row--accepted { - display: flex; -} - -/* Configure row: [delight ▾] [input] [×3] [Go →] */ -.live-demo-ctx-pill { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 5px 10px; - background: var(--color-ink); - color: var(--color-paper); - border: 0; - border-radius: 6px; - font-family: var(--font-body); - font-size: 12px; - font-weight: 500; - cursor: pointer; - white-space: nowrap; -} -.live-demo-ctx-pill-caret { font-size: 9px; opacity: 0.7; margin-left: 2px; } -.live-demo-ctx-input { - display: inline-flex; - align-items: center; - flex: 1; - min-width: 180px; - padding: 5px 8px; - font-family: var(--font-body); - font-size: 12px; - color: var(--color-ink); -} -.live-demo-ctx-caret { - display: inline-block; - width: 1px; - height: 13px; - background: var(--color-ink); - margin-left: 2px; - animation: liveDemoCaret 1s steps(1) infinite; -} -@keyframes liveDemoCaret { - 50% { opacity: 0; } -} -.live-demo-ctx-count { - padding: 4px 6px; - background: transparent; - border: 1px solid var(--color-mist); - border-radius: 5px; - font-family: var(--font-mono); - font-size: 11px; - font-weight: 600; - color: var(--color-ash); - cursor: pointer; -} -.live-demo-ctx-go { - padding: 5px 12px; - background: var(--color-accent); - color: var(--color-paper); - border: 0; - border-radius: 6px; - font-family: var(--font-body); - font-size: 12px; - font-weight: 600; - cursor: pointer; - white-space: nowrap; -} - -/* Generating row */ -.live-demo-ctx-row--generating { - gap: 10px; - padding: 4px 12px 4px 6px; - font-family: var(--font-body); - font-size: 12px; - color: var(--color-charcoal); -} -.live-demo-ctx-spinner { - width: 14px; - height: 14px; - border-radius: 50%; - border: 1.5px solid var(--color-mist); - border-top-color: var(--color-accent); - animation: liveDemoSpin 700ms linear infinite; -} -@keyframes liveDemoSpin { to { transform: rotate(360deg); } } - -/* Cycling row */ -.live-demo-ctx-row--cycling { gap: 2px; padding: 2px; } -.live-demo-ctx-nav { - padding: 4px 10px; - background: transparent; - border: 0; - border-radius: 5px; - color: var(--color-charcoal); - font-size: 14px; - cursor: pointer; -} -.live-demo-ctx-counter { - font-family: var(--font-mono); - font-size: 11px; - color: var(--color-ink); - padding: 0 6px; - min-width: 40px; - text-align: center; -} -.live-demo-ctx-divider { - width: 1px; - height: 18px; - background: var(--color-mist); - margin: 0 4px; -} -.live-demo-ctx-discard { - padding: 4px 10px; - background: transparent; - border: 0; - border-radius: 5px; - color: var(--color-ash); - font-size: 13px; - cursor: pointer; -} -.live-demo-ctx-accept { - padding: 5px 14px; - background: var(--color-ink); - color: var(--color-paper); - border: 0; - border-radius: 6px; - font-family: var(--font-body); - font-size: 12px; - font-weight: 600; - cursor: pointer; -} - -/* Accepted confirmation row */ -.live-demo-ctx-row--accepted { - gap: 8px; - padding: 6px 14px; - font-family: var(--font-body); - font-size: 12px; - color: oklch(45% 0.18 145); -} - -/* Phase caption under the frame */ -.live-demo-caption { - display: flex; - align-items: baseline; - gap: 10px; - font-family: var(--font-mono); - font-size: 11px; - letter-spacing: 0.12em; - text-transform: uppercase; - color: var(--color-ash); -} -.live-demo-caption::before { - content: ""; - display: inline-block; - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--color-accent); - animation: liveDemoPulse 1.6s var(--ease-out) infinite; -} -@keyframes liveDemoPulse { - 0%, 100% { opacity: 1; transform: scale(1); } - 50% { opacity: 0.4; transform: scale(0.7); } -} -.live-demo-caption-label { color: var(--color-ink); } - -/* Supporting row under the demo */ -.live-demo-support { - display: flex; - flex-direction: column; - gap: var(--spacing-md); - align-self: stretch; -} -.live-demo-support-cell { - display: grid; - grid-template-columns: 1fr; - gap: 4px; - padding-bottom: var(--spacing-md); - border-bottom: 1px dashed var(--color-mist); -} -.live-demo-support-cell:last-child { - border-bottom: 0; - padding-bottom: 0; -} -.live-demo-support-k { - font-family: var(--font-mono); - font-size: 12px; - font-weight: 600; - letter-spacing: 0.16em; - text-transform: uppercase; - color: var(--color-accent); -} -.live-demo-support-v { - font-family: var(--font-body); - font-size: 14px; - line-height: 1.55; - color: var(--color-ink); -} -.live-demo-support-v code { - font-family: var(--font-mono); - font-size: 0.875em; - background: var(--color-mist); - padding: 1px 6px; - border-radius: 3px; - color: var(--color-ink); -} - -@media (max-width: 760px) { - .live-demo-frame { aspect-ratio: 4 / 5; } - .live-demo-target { width: 90%; } - .live-demo-bar { font-size: 11px; min-height: 32px; } - .live-demo-support { grid-template-columns: 1fr; gap: var(--spacing-md); } -} - -@media (prefers-reduced-motion: reduce) { - .live-demo-cursor, - .live-demo-outline, - .live-demo-variant, - .live-demo-annotations, - .live-demo-stroke path { - transition: none !important; - animation: none !important; - } - .live-demo-caption::before { animation: none; } -} /* ============================================ LANGUAGE section — Palette / Periodic tabs @@ -3295,7 +2771,6 @@ align-items: flex-end; justify-content: space-between; gap: var(--spacing-lg); - margin-bottom: var(--spacing-xl); } .language-intro-row .section-lead { @@ -3449,224 +2924,6 @@ } } -/* ============================================ - LIVE MODE — skeleton page + richer variants - ============================================ */ - -/* Skeleton elements behind the target card — suggest "this is a real page" */ -.live-demo-skeleton { - position: absolute; - inset: 24px; - display: flex; - flex-direction: column; - gap: 14px; - opacity: 0.3; - pointer-events: none; - filter: blur(0.3px); -} -.live-demo-skel-nav { - display: flex; - align-items: center; - gap: 10px; - padding-bottom: 12px; - border-bottom: 1px solid var(--color-mist); -} -.live-demo-skel-logo { - width: 22px; - height: 22px; - border-radius: 4px; - background: var(--color-charcoal); -} -.live-demo-skel-link { - width: 48px; - height: 8px; - border-radius: 2px; - background: var(--color-mist); -} -.live-demo-skel-cta { - margin-left: auto; - width: 72px; - height: 24px; - border-radius: 4px; - background: var(--color-charcoal); -} -.live-demo-skel-heading { - width: 60%; - height: 18px; - border-radius: 3px; - background: var(--color-mist); - margin-top: 20px; -} -.live-demo-skel-line { - height: 8px; - border-radius: 2px; - background: var(--color-mist); -} -.live-demo-skel-line--short { width: 40%; } - -/* Target stays above skeleton */ -.live-demo-target { position: relative; z-index: 1; } - -/* ---- Card variants — sharper, more distinct ---- */ - -/* Variant 1: magazine column — italic serif, rule lines, off-white */ -.live-demo-card--v1 { - background: var(--color-cream); - padding: 18px 22px; - border: 0; - border-top: 3px solid var(--color-ink); - border-radius: 0; - gap: 6px; -} -.live-demo-card--v1 .live-demo-card-kicker { - color: var(--color-accent); - font-family: var(--font-mono); - font-size: 10px; - letter-spacing: 0.28em; -} -.live-demo-card--v1 h3 { - font-family: var(--font-display); - font-style: italic; - font-weight: 400; - font-size: 28px; - line-height: 1.05; - letter-spacing: -0.01em; - margin-top: 4px; -} -.live-demo-card--v1 h3 em { color: var(--color-accent); font-style: italic; } -.live-demo-card--v1 p { - font-family: var(--font-display); - font-style: italic; - font-size: 14px; - line-height: 1.55; - color: var(--color-charcoal); -} -.live-demo-card--v1 button { - font-family: var(--font-body); - align-self: flex-start; - margin-top: 6px; - background: transparent; - color: var(--color-ink); - border: 0; - border-bottom: 1.5px solid var(--color-ink); - padding: 4px 0; - border-radius: 0; - letter-spacing: 0.08em; -} - -/* Variant 2: brutalist ticket — thick ink block, stamp glyph, mono */ -.live-demo-card--v2 { - position: relative; - background: var(--color-ink); - color: var(--color-paper); - padding: 20px 24px; - border: 0; - border-radius: 0; - overflow: hidden; - gap: 8px; -} -.live-demo-card--v2::before { - content: ""; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 6px; - background: var(--color-accent); -} -.live-demo-card-stamp { - position: absolute; - top: 14px; - right: 18px; - font-size: 28px; - line-height: 1; - color: var(--color-accent); - transform: rotate(-8deg); -} -.live-demo-card--v2 .live-demo-card-kicker { - color: var(--color-accent); - letter-spacing: 0.3em; - font-weight: 600; -} -.live-demo-card--v2 h3 { - font-family: var(--font-body); - font-weight: 700; - font-size: 24px; - line-height: 1.1; - color: var(--color-paper); - letter-spacing: -0.02em; -} -.live-demo-card--v2 button { - font-family: var(--font-mono); - font-weight: 500; - align-self: flex-start; - margin-top: 6px; - background: var(--color-accent); - color: var(--color-paper); - border: 0; - border-radius: 0; - padding: 9px 14px; - letter-spacing: 0.04em; - text-transform: none; - font-size: 12px; -} - -/* Variant 3: playful ticket — accent background, sticker stars, postcard vibe */ -.live-demo-card--v3 { - position: relative; - background: - radial-gradient(circle at 20% 80%, oklch(92% 0.08 350) 0, transparent 45%), - var(--color-cream); - border: 1px dashed var(--color-accent); - border-radius: 10px; - padding: 22px 24px 20px; - gap: 8px; -} -.live-demo-card-sticker { - position: absolute; - top: 10px; - right: 14px; - display: flex; - gap: 3px; - color: var(--color-accent); - font-size: 14px; - line-height: 1; - transform: rotate(6deg); -} -.live-demo-card--v3 .live-demo-card-kicker { - font-family: var(--font-display); - font-style: italic; - font-weight: 400; - font-size: 13px; - letter-spacing: 0.04em; - color: var(--color-accent-hover); - text-transform: none; -} -.live-demo-card--v3 h3 { - font-family: var(--font-display); - font-weight: 400; - font-size: 22px; - line-height: 1.2; - color: var(--color-ink); -} -.live-demo-card--v3 button { - display: inline-flex; - align-items: center; - gap: 6px; - align-self: flex-start; - background: var(--color-ink); - color: var(--color-paper); - border: 0; - border-radius: 999px; - padding: 10px 18px; - font-family: var(--font-body); - font-weight: 500; - letter-spacing: 0.06em; - text-transform: none; - font-size: 12px; - margin-top: 6px; -} - /* ============================================ ANTIDOTE — visual-first wall of three ============================================ */ @@ -4014,6 +3271,21 @@ max-width: none; } +.gallery-stack-link { + display: inline-flex; + align-self: center; + margin-top: var(--spacing-sm); + font-family: var(--font-body); + font-size: 0.82rem; + color: var(--color-ink); + text-decoration: underline; + text-underline-offset: 3px; + text-decoration-color: var(--color-accent); + transition: color 0.12s ease; +} + +.gallery-stack-link:hover { color: var(--color-accent); } + @media (max-width: 860px) { .why-visual--antidote { grid-template-columns: 1fr; } } diff --git a/public/index.html b/public/index.html index ed023233d..8285bda85 100644 --- a/public/index.html +++ b/public/index.html @@ -60,7 +60,7 @@ Home Docs Anti-Patterns - Live + Live Overlay @@ -288,13 +288,14 @@ Purple gradients. Glassmorphism. "Boost your productivity."
- + -

/impeccable teach · /impeccable document · /impeccable shape