commit dea27fc1da114db9ce9d0d2e69a787a6b4e32073 Author: Chen Gu Date: Thu Apr 23 07:07:08 2026 +0800 feat: bootstrap spectra lab front-end concept diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53e30e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +spectra-lab-local.png +.DS_Store diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..99a1cfb --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,36 @@ +# Spectra Lab 设计语言 + +## 关键词 +- future luxe +- spectral glow +- glassmorphism +- editorial typography +- cinematic depth +- interactive gallery + +## 视觉原则 +1. 黑底 + 彩色光谱高光 +2. 大标题排版要有海报感 +3. 卡片不做普通 SaaS 面板,要像展馆展签 +4. 动效要柔和、缓动、分层,不要廉价弹跳 +5. 让页面每一屏都有明确主角 + +## 首版结构 +- 顶部导航:极简悬浮 +- Hero:巨大标题、动态背景、主 CTA、次级信息 +- Live Metrics:用设计化仪表呈现虚构实时数据 +- Scene Gallery:三到四个主场景卡片 +- Manifesto:更情绪化的品牌段落 +- Footer:简短收束 + +## 动效策略 +- 鼠标跟随高光 +- 轻量视差 +- 背景网格缓慢漂移 +- 卡片 hover 的透视和边缘发光 +- KPI 数字缓动增长 + +## 响应式原则 +- 桌面端强调沉浸和横向层次 +- 移动端保留光效,但结构收成单列 +- 保证标题、按钮、卡片在窄屏仍有冲击力 diff --git a/README.md b/README.md new file mode 100644 index 0000000..5609a47 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# Spectra Lab + +一个以视觉表现力为核心的实验性 Web App。 + +## 项目目标 +- 优先级一:前端显示效果惊艳、与众不同、有设计感 +- 优先级二:保持可交互、可演示、可继续扩展 +- 优先级三:10 小时内完成可交付版本 + +## 当前技术策略 +- 采用原生 HTML / CSS / JavaScript +- 不依赖重型框架,优先保证视觉落地速度与部署简单度 +- 重点投入在:动效、排版、层次、质感、氛围、交互反馈 + +## 首版概念 +Spectra Lab:未来感数字展馆 / 灵感实验室。 +用户可以在一个沉浸式页面里浏览多个“光谱场景”: +- Hero 主舞台 +- 动态数据仪表 +- 场景卡片矩阵 +- 时间线/展陈区 +- 可切换主题氛围 + +## 开发规程(本轮) +1. 先完成视觉概念统一与页面骨架 +2. 再补高价值动效与交互 +3. 然后做响应式适配 +4. 最后做可运行验证与演示收口 + +## 当前文件 +- `index.html`:页面结构 +- `styles.css`:视觉系统与动效 +- `app.js`:交互、状态、动画驱动 +- `DESIGN.md`:设计语言与构图原则 diff --git a/app.js b/app.js new file mode 100644 index 0000000..ede35ae --- /dev/null +++ b/app.js @@ -0,0 +1,168 @@ +const pointerGlow = document.getElementById('pointerGlow'); +const themeToggle = document.getElementById('themeToggle'); +const themeLabel = document.getElementById('themeLabel'); +const sceneCards = document.querySelectorAll('.scene-card'); +const counters = document.querySelectorAll('[data-counter]'); +const sceneDetailTitle = document.getElementById('sceneDetailTitle'); +const sceneDetailCopy = document.getElementById('sceneDetailCopy'); +const particleCanvas = document.getElementById('particleCanvas'); + +const themes = [ + { className: '', label: 'Aurora' }, + { className: 'theme-ember', label: 'Ember' }, + { className: 'theme-tide', label: 'Tide' }, +]; + +let themeIndex = 0; + +function setTheme(index) { + document.body.classList.remove('theme-ember', 'theme-tide'); + const theme = themes[index]; + if (theme.className) { + document.body.classList.add(theme.className); + } + themeLabel.textContent = theme.label; +} + +function animateCounter(element) { + const target = Number(element.dataset.counter || 0); + const duration = 1400; + const start = performance.now(); + + function step(now) { + const progress = Math.min((now - start) / duration, 1); + const eased = 1 - Math.pow(1 - progress, 3); + element.textContent = Math.round(target * eased); + if (progress < 1) requestAnimationFrame(step); + } + + requestAnimationFrame(step); +} + +function mountParticleField() { + if (!particleCanvas) return; + const ctx = particleCanvas.getContext('2d'); + if (!ctx) return; + + const particles = []; + const particleCount = 48; + let width = 0; + let height = 0; + + function resize() { + width = window.innerWidth; + height = window.innerHeight; + particleCanvas.width = width * window.devicePixelRatio; + particleCanvas.height = height * window.devicePixelRatio; + particleCanvas.style.width = `${width}px`; + particleCanvas.style.height = `${height}px`; + ctx.setTransform(window.devicePixelRatio, 0, 0, window.devicePixelRatio, 0, 0); + } + + function seed() { + particles.length = 0; + for (let i = 0; i < particleCount; i += 1) { + particles.push({ + x: Math.random() * width, + y: Math.random() * height, + r: Math.random() * 1.8 + 0.8, + vx: (Math.random() - 0.5) * 0.28, + vy: (Math.random() - 0.5) * 0.28, + }); + } + } + + function draw() { + ctx.clearRect(0, 0, width, height); + + particles.forEach((particle, index) => { + particle.x += particle.vx; + particle.y += particle.vy; + + if (particle.x < -20) particle.x = width + 20; + if (particle.x > width + 20) particle.x = -20; + if (particle.y < -20) particle.y = height + 20; + if (particle.y > height + 20) particle.y = -20; + + ctx.beginPath(); + ctx.fillStyle = 'rgba(255,255,255,0.38)'; + ctx.arc(particle.x, particle.y, particle.r, 0, Math.PI * 2); + ctx.fill(); + + for (let j = index + 1; j < particles.length; j += 1) { + const other = particles[j]; + const dx = particle.x - other.x; + const dy = particle.y - other.y; + const distance = Math.sqrt(dx * dx + dy * dy); + if (distance < 130) { + ctx.beginPath(); + ctx.strokeStyle = `rgba(130, 180, 255, ${0.12 * (1 - distance / 130)})`; + ctx.moveTo(particle.x, particle.y); + ctx.lineTo(other.x, other.y); + ctx.stroke(); + } + } + }); + + requestAnimationFrame(draw); + } + + resize(); + seed(); + draw(); + window.addEventListener('resize', () => { + resize(); + seed(); + }); +} + +window.addEventListener('pointermove', (event) => { + pointerGlow.style.transform = `translate(${event.clientX}px, ${event.clientY}px)`; +}); + +sceneCards.forEach((card) => { + card.addEventListener('pointermove', (event) => { + const rect = card.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = event.clientY - rect.top; + const rotateX = ((y / rect.height) - 0.5) * -8; + const rotateY = ((x / rect.width) - 0.5) * 10; + + card.style.setProperty('--mx', `${(x / rect.width) * 100}%`); + card.style.setProperty('--my', `${(y / rect.height) * 100}%`); + card.style.transform = `translateY(-6px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`; + }); + + card.addEventListener('pointerleave', () => { + card.style.transform = ''; + }); + + card.addEventListener('click', () => { + sceneCards.forEach((item) => item.classList.remove('is-active')); + card.classList.add('is-active'); + sceneDetailTitle.textContent = card.dataset.sceneTitle || ''; + sceneDetailCopy.textContent = card.dataset.sceneCopy || ''; + }); +}); + +themeToggle.addEventListener('click', () => { + themeIndex = (themeIndex + 1) % themes.length; + setTheme(themeIndex); +}); + +const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + animateCounter(entry.target); + observer.unobserve(entry.target); + } + }); + }, + { threshold: 0.4 }, +); + +counters.forEach((counter) => observer.observe(counter)); +sceneCards[0]?.classList.add('is-active'); +setTheme(themeIndex); +mountParticleField(); diff --git a/index.html b/index.html new file mode 100644 index 0000000..cd905ba --- /dev/null +++ b/index.html @@ -0,0 +1,173 @@ + + + + + + Spectra Lab + + + + + + + + +
+
+
+
+
+ +
+
+ +
+

Spectra Lab

+

Digital Atmosphere Prototype

+
+
+ + +
+ +
+
+
+

A cinematic front-end experience

+

+ 不只是一个页面,
+ 而是一座会呼吸的
+ 光谱展馆。 +

+

+ Spectra Lab 把动态网格、玻璃拟态、彩色流体光效与编辑感排版叠在一起,做成一个可以真实浏览、真实交互、真实惊艳的 Web App 原型。 +

+ +
+ +
+
+
+
+

Signal Cluster

+

07

+ active scenes +
+
+

Ambient Index

+ 0 + cinematic density +
+
+

Pulse Drift

+ 0 + motion vectors +
+
+

Interface Bloom

+ 0 + responsive nodes +
+
+
+ +
+
+

Live aesthetic telemetry

+

把“设计感”拆成可感知的前端语言

+
+
+
+ Layer Depth + 9.4 +

通过前景、中景、背景与微粒噪声叠出空间感。

+
+
+ Motion Quality + 92% +

减少廉价跳变,用缓动、漂移与轻量视差维持高级感。

+
+
+ Visual Contrast + 4 Layers +

用霓光、暗底、玻璃边缘和字体重量做视觉层次。

+
+
+ Current Mode + Aurora +

支持切换不同光谱主题,让同一界面拥有不同情绪。

+
+
+
+ + + +
+

Manifesto

+

真正惊艳的前端,不是堆特效,而是让特效服务于气质。

+

+ 这个原型故意把功能压到第二位,把所有预算投给视觉叙事:版式像海报,交互像展陈,背景像呼吸,按钮像可触摸的材质。主人要的不是“能用就行”的站,而是第一眼就记得住的作品。 +

+
+
+ + + + + + diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..89baec6 --- /dev/null +++ b/styles.css @@ -0,0 +1,624 @@ +:root { + --bg: #070816; + --bg-soft: rgba(255, 255, 255, 0.08); + --panel: rgba(13, 17, 38, 0.5); + --panel-strong: rgba(17, 21, 46, 0.72); + --text: #f3f5ff; + --muted: rgba(230, 236, 255, 0.72); + --border: rgba(255, 255, 255, 0.14); + --shadow: 0 24px 80px rgba(0, 0, 0, 0.45); + --accent-a: #7c5cff; + --accent-b: #2be4ff; + --accent-c: #ff6bd6; + --accent-d: #ffd166; +} + +body.theme-ember { + --accent-a: #ff7a18; + --accent-b: #ff3cac; + --accent-c: #7b61ff; + --accent-d: #ffe66d; +} + +body.theme-tide { + --accent-a: #4facfe; + --accent-b: #00f2fe; + --accent-c: #00ffa3; + --accent-d: #b2f7ef; +} + +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + min-height: 100vh; + font-family: 'Inter', 'Noto Sans SC', sans-serif; + color: var(--text); + background: + radial-gradient(circle at top left, rgba(124, 92, 255, 0.16), transparent 30%), + radial-gradient(circle at top right, rgba(43, 228, 255, 0.12), transparent 28%), + linear-gradient(180deg, #050611 0%, #090b18 45%, #06070d 100%); + overflow-x: hidden; +} + +body::before { + content: ''; + position: fixed; + inset: 0; + background-image: + linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px); + background-size: 72px 72px; + mask-image: radial-gradient(circle at center, black 40%, transparent 90%); + opacity: 0.28; + pointer-events: none; +} + +.noise { + position: fixed; + inset: 0; + opacity: 0.08; + pointer-events: none; + background-image: + radial-gradient(circle at 20% 20%, rgba(255,255,255,0.22) 0 1px, transparent 1px), + radial-gradient(circle at 70% 35%, rgba(255,255,255,0.16) 0 1px, transparent 1px), + radial-gradient(circle at 30% 80%, rgba(255,255,255,0.12) 0 1px, transparent 1px); + background-size: 180px 180px; + mix-blend-mode: screen; +} + +.gradient-orb { + position: fixed; + width: 38rem; + height: 38rem; + border-radius: 50%; + filter: blur(40px); + opacity: 0.28; + pointer-events: none; + animation: drift 14s ease-in-out infinite alternate; +} + +.orb-a { + top: -8rem; + left: -10rem; + background: radial-gradient(circle, var(--accent-a), transparent 62%); +} + +.orb-b { + top: 24rem; + right: -12rem; + background: radial-gradient(circle, var(--accent-b), transparent 60%); + animation-duration: 18s; +} + +.orb-c { + bottom: -10rem; + left: 22%; + background: radial-gradient(circle, var(--accent-c), transparent 58%); + animation-duration: 22s; +} + +.pointer-glow { + position: fixed; + width: 18rem; + height: 18rem; + margin-left: -9rem; + margin-top: -9rem; + border-radius: 50%; + pointer-events: none; + background: radial-gradient(circle, rgba(255,255,255,0.16), rgba(255,255,255,0.02) 45%, transparent 72%); + mix-blend-mode: screen; + opacity: 0.75; + z-index: 1; +} + +.particle-canvas { + position: fixed; + inset: 0; + width: 100%; + height: 100%; + pointer-events: none; + opacity: 0.55; + z-index: 0; +} + +.topbar, +.section, +.footer { + position: relative; + z-index: 2; +} + +.topbar { + width: min(1180px, calc(100% - 32px)); + margin: 20px auto 0; + padding: 16px 18px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +.brand { + display: flex; + align-items: center; + gap: 14px; +} + +.brand-mark { + width: 14px; + height: 14px; + border-radius: 50%; + background: linear-gradient(135deg, var(--accent-b), var(--accent-c)); + box-shadow: 0 0 18px var(--accent-b), 0 0 40px var(--accent-c); +} + +.brand-eyebrow, +.brand-sub, +.eyebrow, +.scene-tag, +.metric-card span, +.footer, +.section-note { + letter-spacing: 0.08em; +} + +.brand-eyebrow, +.eyebrow { + margin: 0; + text-transform: uppercase; + color: rgba(255,255,255,0.74); + font-size: 0.74rem; +} + +.brand-sub { + margin: 2px 0 0; + font-size: 0.75rem; + color: rgba(255,255,255,0.52); +} + +.nav-links { + display: flex; + gap: 24px; +} + +.nav-links a, +.footer { + color: rgba(255,255,255,0.65); + text-decoration: none; + font-size: 0.92rem; +} + +.ghost-button, +.primary-button, +.secondary-button { + border-radius: 999px; + transition: transform 180ms ease, box-shadow 180ms ease, background 180ms ease; +} + +.ghost-button { + border: 1px solid rgba(255,255,255,0.14); + background: rgba(255,255,255,0.05); + color: var(--text); + padding: 12px 16px; + cursor: pointer; +} + +.primary-button, +.secondary-button { + text-decoration: none; + padding: 14px 20px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.primary-button { + background: linear-gradient(135deg, var(--accent-a), var(--accent-c)); + color: white; + box-shadow: 0 18px 40px rgba(124, 92, 255, 0.28); +} + +.secondary-button { + color: var(--text); + border: 1px solid rgba(255,255,255,0.16); + background: rgba(255,255,255,0.05); +} + +.glass-panel { + background: linear-gradient(180deg, rgba(255,255,255,0.1), rgba(255,255,255,0.04)); + border: 1px solid var(--border); + box-shadow: var(--shadow); + backdrop-filter: blur(24px) saturate(160%); + -webkit-backdrop-filter: blur(24px) saturate(160%); +} + +.section { + width: min(1180px, calc(100% - 32px)); + margin: 0 auto; + padding: 84px 0; +} + +.hero { + display: grid; + grid-template-columns: 1.08fr 0.92fr; + align-items: center; + gap: 42px; + min-height: calc(100vh - 110px); +} + +.hero-copy h1 { + margin: 16px 0 18px; + font-size: clamp(3rem, 7vw, 6.25rem); + line-height: 0.95; + letter-spacing: -0.045em; +} + +.hero-text { + max-width: 40rem; + font-size: 1.05rem; + line-height: 1.8; + color: var(--muted); +} + +.hero-actions { + display: flex; + flex-wrap: wrap; + gap: 14px; + margin-top: 28px; +} + +.hero-stage { + position: relative; + min-height: 620px; + border-radius: 34px; + overflow: hidden; + padding: 30px; +} + +.stage-grid, +.stage-ring { + position: absolute; + inset: 0; +} + +.stage-grid { + background-image: + linear-gradient(rgba(255,255,255,0.07) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,0.07) 1px, transparent 1px); + background-size: 36px 36px; + mask-image: radial-gradient(circle at center, black 45%, transparent 88%); + animation: pulseGrid 12s linear infinite; +} + +.stage-ring { + inset: 12%; + border-radius: 50%; + border: 1px solid rgba(255,255,255,0.12); + box-shadow: + 0 0 40px rgba(124,92,255,0.22), + inset 0 0 40px rgba(43,228,255,0.12); +} + +.stage-core { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + width: 16rem; + height: 16rem; + border-radius: 50%; + display: grid; + place-items: center; + text-align: center; + background: radial-gradient(circle, rgba(255,255,255,0.16), rgba(255,255,255,0.03)); + border: 1px solid rgba(255,255,255,0.14); + box-shadow: 0 0 60px rgba(124,92,255,0.22); +} + +.stage-core p, +.stage-core span { + margin: 0; + color: rgba(255,255,255,0.66); + text-transform: uppercase; + font-size: 0.78rem; + letter-spacing: 0.1em; +} + +.stage-core h2 { + margin: 6px 0; + font-size: 5rem; + line-height: 1; +} + +.floating-card { + position: absolute; + min-width: 180px; + padding: 18px 18px 16px; + border-radius: 24px; + background: linear-gradient(180deg, rgba(255,255,255,0.14), rgba(255,255,255,0.05)); + border: 1px solid rgba(255,255,255,0.16); + box-shadow: 0 18px 40px rgba(0,0,0,0.28); + backdrop-filter: blur(18px); +} + +.floating-card p, +.floating-card span { + margin: 0; + color: rgba(255,255,255,0.66); + font-size: 0.78rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.floating-card strong { + display: block; + margin: 8px 0 6px; + font-size: 2.5rem; +} + +.card-alpha { top: 8%; left: 4%; animation: floatCard 8s ease-in-out infinite; } +.card-beta { right: 4%; top: 18%; animation: floatCard 11s ease-in-out infinite reverse; } +.card-gamma { left: 12%; bottom: 8%; animation: floatCard 9s ease-in-out infinite 0.8s; } + +.section-heading { + max-width: 52rem; + margin-bottom: 28px; +} + +.section-heading h2, +.manifesto h2 { + margin: 12px 0 0; + font-size: clamp(2rem, 4vw, 3.6rem); + line-height: 1.05; + letter-spacing: -0.04em; +} + +.split { + display: flex; + justify-content: space-between; + gap: 20px; + align-items: end; +} + +.section-note, +.manifesto p, +.metric-card p, +.scene-card p { + color: var(--muted); + line-height: 1.75; +} + +.metric-grid, +.gallery-grid { + display: grid; + gap: 18px; +} + +.metric-grid { + grid-template-columns: repeat(4, 1fr); +} + +.metric-card, +.scene-card, +.manifesto { + border-radius: 28px; +} + +.metric-card { + padding: 24px; + min-height: 220px; +} + +.metric-card strong { + display: block; + margin: 20px 0 14px; + font-size: clamp(2rem, 4vw, 3.5rem); + letter-spacing: -0.05em; +} + +.accent-card { + background: linear-gradient(135deg, rgba(124,92,255,0.2), rgba(43,228,255,0.1), rgba(255,255,255,0.06)); +} + +.gallery-grid { + grid-template-columns: 1.2fr 1fr 1fr; + grid-auto-rows: 250px; +} + +.scene-card { + position: relative; + overflow: hidden; + padding: 24px; + transform-style: preserve-3d; + transition: transform 220ms ease, border-color 220ms ease, box-shadow 220ms ease; +} + +.scene-card::before { + content: ''; + position: absolute; + inset: -20%; + background: radial-gradient(circle at var(--mx, 50%) var(--my, 50%), rgba(255,255,255,0.16), transparent 35%); + opacity: 0; + transition: opacity 220ms ease; +} + +.scene-card:hover { + transform: translateY(-6px) rotateX(4deg) rotateY(-4deg); + border-color: rgba(255,255,255,0.24); + box-shadow: 0 24px 70px rgba(0,0,0,0.34), 0 0 30px rgba(124,92,255,0.14); +} + +.scene-card:hover::before { + opacity: 1; +} + +.scene-card.is-active { + border-color: rgba(255,255,255,0.3); + box-shadow: 0 24px 70px rgba(0,0,0,0.34), 0 0 34px rgba(124,92,255,0.18); +} + +.scene-large { + grid-row: span 2; + min-height: 518px; + background: + linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.04)), + radial-gradient(circle at top right, rgba(43,228,255,0.24), transparent 35%), + radial-gradient(circle at bottom left, rgba(124,92,255,0.24), transparent 34%); +} + +.scene-tag { + color: rgba(255,255,255,0.72); + font-size: 0.74rem; + text-transform: uppercase; +} + +.scene-card h3 { + margin: 18px 0 10px; + font-size: 1.8rem; +} + +.scene-detail { + margin-top: 20px; + padding: 24px 26px; +} + +.scene-detail h3 { + margin: 10px 0 6px; + font-size: clamp(1.6rem, 3vw, 2.4rem); +} + +.manifesto { + padding: 34px; + margin-top: 14px; +} + +.footer { + width: min(1180px, calc(100% - 32px)); + margin: 0 auto; + padding: 26px 0 44px; + display: flex; + justify-content: space-between; + gap: 16px; +} + +.ghost-button:hover, +.primary-button:hover, +.secondary-button:hover { + transform: translateY(-2px); +} + +@keyframes drift { + 0% { transform: translate3d(0, 0, 0) scale(1); } + 100% { transform: translate3d(24px, -32px, 0) scale(1.08); } +} + +@keyframes floatCard { + 0%, 100% { transform: translateY(0px); } + 50% { transform: translateY(-12px); } +} + +@keyframes pulseGrid { + 0% { transform: scale(1) rotate(0deg); opacity: 0.72; } + 50% { transform: scale(1.04) rotate(0.8deg); opacity: 0.9; } + 100% { transform: scale(1) rotate(0deg); opacity: 0.72; } +} + +@media (max-width: 1100px) { + .hero, + .metric-grid, + .gallery-grid, + .split, + .footer, + .topbar { + grid-template-columns: 1fr !important; + flex-direction: column; + align-items: flex-start; + } + + .hero { + min-height: auto; + padding-top: 40px; + } + + .metric-grid { + display: grid; + grid-template-columns: repeat(2, 1fr) !important; + } + + .gallery-grid { + display: grid; + grid-template-columns: 1fr 1fr !important; + } + + .scene-large { + grid-column: 1 / -1; + grid-row: auto; + min-height: 320px; + } + + .hero-stage { + min-height: 500px; + width: 100%; + } +} + +@media (max-width: 720px) { + .section { + padding: 64px 0; + } + + .topbar { + margin-top: 14px; + } + + .nav-links { + display: none; + } + + .metric-grid, + .gallery-grid { + grid-template-columns: 1fr !important; + } + + .hero-copy h1 { + font-size: clamp(2.7rem, 14vw, 4.2rem); + } + + .hero-stage { + min-height: 460px; + padding: 18px; + } + + .stage-core { + width: 12rem; + height: 12rem; + } + + .stage-core h2 { + font-size: 3.7rem; + } + + .floating-card { + min-width: 140px; + padding: 14px; + } + + .floating-card strong { + font-size: 2rem; + } + + .manifesto { + padding: 24px; + } + + .footer { + padding-bottom: 30px; + } +}