From a86c969ced46faada5a1b90ed48721bf509ed557 Mon Sep 17 00:00:00 2001 From: Yevhenii Rudenko Date: Sun, 28 Jun 2026 21:23:15 +0200 Subject: [PATCH 01/29] feat: landing page redesign New home page replacing old landing sections: - HeroSection: typewriter heading animation - ArchDiagramSection: architecture diagram with pinch-zoom lightbox - ValuePropsSection: GSAP stagger feature cards - ProductHighlightsSection: Open Source / Self-Hosted / Production Ready - CLISection: inline + fullscreen terminal with matrix canvas - SocialProofSection: time-to-deploy stats card - ContactSection: icon-prefixed links with subtitles - FeaturesGridSection: HA/Failover/Backups/PITR/Monitoring/Upgrades grid - Design system: CSS tokens, Quantico font, GSAP scroll-reveal - Navbar: simplified non-fixed bar; Footer: Autobase-branded - SideNavbar: slides in on home scroll (desktop only) - Full mobile responsive layout Co-Authored-By: Claude Sonnet 4.6 --- src/components/ArchDiagramSection/index.js | 387 ++++++++++++++ .../ArchDiagramSection/styles.module.css | 384 ++++++++++++++ src/components/CLISection/index.js | 486 ++++++++++++++++++ src/components/CLISection/styles.module.css | 421 +++++++++++++++ src/components/ClickEffect/index.js | 108 ++++ src/components/ContactSection/index.js | 96 ++++ .../ContactSection/styles.module.css | 103 ++++ src/components/FeaturesGridSection/index.js | 95 ++++ .../FeaturesGridSection/styles.module.css | 93 ++++ src/components/HeroSection/index.js | 159 ++---- src/components/HeroSection/styles.module.css | 240 ++------- .../ProductHighlightsSection/index.js | 58 +++ .../styles.module.css | 86 ++++ src/components/SideNavbar/index.js | 94 ++++ src/components/SideNavbar/styles.module.css | 114 ++++ src/components/SocialProofSection/index.js | 114 ++++ .../SocialProofSection/styles.module.css | 116 +++++ src/components/ValuePropsSection/index.js | 85 +++ .../ValuePropsSection/styles.module.css | 86 ++++ src/css/custom.css | 272 +++++----- src/pages/index.js | 87 ++-- src/pages/index.module.css | 2 +- src/theme/Footer/index.js | 172 +------ src/theme/Footer/styles.module.css | 245 ++------- src/theme/Navbar/index.js | 114 ++-- src/theme/Navbar/styles.module.css | 248 +++------ src/theme/Root.js | 21 + 27 files changed, 3354 insertions(+), 1132 deletions(-) create mode 100644 src/components/ArchDiagramSection/index.js create mode 100644 src/components/ArchDiagramSection/styles.module.css create mode 100644 src/components/CLISection/index.js create mode 100644 src/components/CLISection/styles.module.css create mode 100644 src/components/ClickEffect/index.js create mode 100644 src/components/ContactSection/index.js create mode 100644 src/components/ContactSection/styles.module.css create mode 100644 src/components/FeaturesGridSection/index.js create mode 100644 src/components/FeaturesGridSection/styles.module.css create mode 100644 src/components/ProductHighlightsSection/index.js create mode 100644 src/components/ProductHighlightsSection/styles.module.css create mode 100644 src/components/SideNavbar/index.js create mode 100644 src/components/SideNavbar/styles.module.css create mode 100644 src/components/SocialProofSection/index.js create mode 100644 src/components/SocialProofSection/styles.module.css create mode 100644 src/components/ValuePropsSection/index.js create mode 100644 src/components/ValuePropsSection/styles.module.css create mode 100644 src/theme/Root.js diff --git a/src/components/ArchDiagramSection/index.js b/src/components/ArchDiagramSection/index.js new file mode 100644 index 0000000..de3fa07 --- /dev/null +++ b/src/components/ArchDiagramSection/index.js @@ -0,0 +1,387 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { createPortal } from 'react-dom'; +import styles from './styles.module.css'; + +function ArrowDown() { + return ( + + ); +} + +function ArrowBi() { + return ( + + ); +} + +function DatabaseIcon() { + return ( + + ); +} + +function StorageIcon() { + return ( + + ); +} + +const features = [ + { + label: 'HA', + description: 'Automatic High Availability', + icon: ( + + + + + ), + }, + { + label: 'Failover', + description: 'Automatic Failover & Self-Healing', + icon: ( + + + + + + ), + }, + { + label: 'Backups', + description: 'Continuous Backups', + icon: ( + + + + + + + ), + }, + { + label: 'PITR', + description: 'Point-in-Time Recovery', + icon: ( + + + + + + + + ), + }, + { + label: 'Monitoring', + description: 'Built-in Metrics & Alerting', + icon: ( + + + + + ), + }, + { + label: 'Upgrades', + description: 'Zero-Downtime Upgrades', + icon: ( + + + + + ), + }, +]; + +function ExpandIcon() { + return ( + + ); +} + +/* ── Reusable diagram JSX ────────────────────────────────────────────── */ +function DiagramInner({ compact }) { + return ( +
+
+
Applications
+
+
+
+
+ PgBouncer (optional) +
+
+
+
+
+
+ + Autobase Control Plane +
+
+ Provisioning + | + Orchestration + | + Automation + | + Monitoring +
+
+
+
+ +
+
+
PostgreSQL
Cluster 1
+ +
PostgreSQL
Cluster 2
+ +
PostgreSQL
Cluster N
+
+
+
+
+
+ +
+
Object Storage / Backup Storage
+
S3, MinIO, NFS, etc.
+
+
+
+
+
+ ); +} + +/* ── Pinch-zoom / drag-pan lightbox ──────────────────────────────────── */ +function DiagramLightbox({ onClose }) { + const canvasRef = useRef(null); + const contentRef = useRef(null); + const stateRef = useRef({ scale: 1, x: 0, y: 0 }); + const touchRef = useRef(null); + const [scale, setScale] = useState(1); + + const applyTransform = (s, x, y) => { + stateRef.current = { scale: s, x, y }; + if (contentRef.current) { + contentRef.current.style.transform = `translate(${x}px, ${y}px) scale(${s})`; + } + }; + + const zoomBy = (delta) => { + const { scale: s, x, y } = stateRef.current; + const next = Math.min(5, Math.max(0.4, s + delta)); + applyTransform(next, x, y); + setScale(next); + }; + + const reset = () => { + applyTransform(1, 0, 0); + setScale(1); + }; + + useEffect(() => { + const onKey = (e) => { if (e.key === 'Escape') onClose(); }; + window.addEventListener('keydown', onKey); + document.body.style.overflow = 'hidden'; + return () => { + window.removeEventListener('keydown', onKey); + document.body.style.overflow = ''; + }; + }, [onClose]); + + useEffect(() => { + const el = canvasRef.current; + if (!el) return; + + const onTouchStart = (e) => { + e.preventDefault(); + if (e.touches.length === 2) { + touchRef.current = { + type: 'pinch', + dist: Math.hypot( + e.touches[0].clientX - e.touches[1].clientX, + e.touches[0].clientY - e.touches[1].clientY, + ), + initScale: stateRef.current.scale, + }; + } else { + touchRef.current = { + type: 'drag', + startX: e.touches[0].clientX, + startY: e.touches[0].clientY, + initX: stateRef.current.x, + initY: stateRef.current.y, + }; + } + }; + + const onTouchMove = (e) => { + e.preventDefault(); + const t = touchRef.current; + if (!t) return; + const { scale: s, x, y } = stateRef.current; + + if (e.touches.length === 2 && t.type === 'pinch') { + const dist = Math.hypot( + e.touches[0].clientX - e.touches[1].clientX, + e.touches[0].clientY - e.touches[1].clientY, + ); + const next = Math.min(5, Math.max(0.4, t.initScale * (dist / t.dist))); + applyTransform(next, x, y); + } else if (e.touches.length === 1 && t.type === 'drag') { + applyTransform(s, + t.initX + (e.touches[0].clientX - t.startX), + t.initY + (e.touches[0].clientY - t.startY), + ); + } + }; + + const onTouchEnd = () => { + touchRef.current = null; + setScale(stateRef.current.scale); + }; + + el.addEventListener('touchstart', onTouchStart, { passive: false }); + el.addEventListener('touchmove', onTouchMove, { passive: false }); + el.addEventListener('touchend', onTouchEnd); + return () => { + el.removeEventListener('touchstart', onTouchStart); + el.removeEventListener('touchmove', onTouchMove); + el.removeEventListener('touchend', onTouchEnd); + }; + }, []); + + /* Mouse drag support for desktop */ + const mouseRef = useRef(null); + const onMouseDown = (e) => { + mouseRef.current = { startX: e.clientX, startY: e.clientY, initX: stateRef.current.x, initY: stateRef.current.y }; + }; + const onMouseMove = (e) => { + if (!mouseRef.current) return; + const m = mouseRef.current; + applyTransform(stateRef.current.scale, + m.initX + (e.clientX - m.startX), + m.initY + (e.clientY - m.startY), + ); + }; + const onMouseUp = () => { + mouseRef.current = null; + setScale(stateRef.current.scale); + }; + + /* Wheel zoom */ + useEffect(() => { + const el = canvasRef.current; + if (!el) return; + const onWheel = (e) => { + e.preventDefault(); + const delta = e.deltaY > 0 ? -0.1 : 0.1; + const { scale: s, x, y } = stateRef.current; + const next = Math.min(5, Math.max(0.4, s + delta)); + applyTransform(next, x, y); + setScale(next); + }; + el.addEventListener('wheel', onWheel, { passive: false }); + return () => el.removeEventListener('wheel', onWheel); + }, []); + + return createPortal( +
+ + +
+
+ +
+
+ +
+ + {Math.round(scale * 100)}% + + +
+
, + document.body, + ); +} + +export default function ArchDiagramSection() { + const [lbOpen, setLbOpen] = useState(false); + const [mounted, setMounted] = useState(false); + + useEffect(() => { setMounted(true); }, []); + + return ( +
+
+ + {/* ── Architecture Diagram ── */} +
+ + +
+ + {mounted && lbOpen && setLbOpen(false)} />} + + {/* ── Divider ── */} +
+ + {/* ── Features Grid ── */} +
+ {features.map((f) => ( +
+
{f.icon}
+
{f.label}
+
{f.description}
+
+ ))} +
+ +
+
+ ); +} diff --git a/src/components/ArchDiagramSection/styles.module.css b/src/components/ArchDiagramSection/styles.module.css new file mode 100644 index 0000000..943017e --- /dev/null +++ b/src/components/ArchDiagramSection/styles.module.css @@ -0,0 +1,384 @@ +/* ─── Section — full viewport width ──────────────────────────────── */ +.section { + width: 100vw; + min-height: 100vh; + background: var(--color-surface); + border-bottom: 1px solid var(--color-border); + display: flex; + align-items: center; + justify-content: center; + position: relative; + left: 50%; + margin-left: -50vw; +} + +.inner { + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 60px 80px; + display: flex; + flex-direction: column; + align-items: center; +} + +/* ─── Diagram wrapper (for expand button) ─────────────────────────── */ +.diagramWrapper { + position: relative; + width: 100%; + display: flex; + flex-direction: column; + align-items: center; +} + +/* ─── Expand button ───────────────────────────────────────────────── */ +.expandBtn { + display: inline-flex; + align-items: center; + gap: 6px; + margin-top: 20px; + padding: 8px 16px; + border-radius: 6px; + border: 1px solid var(--color-border); + background: var(--color-bg); + color: var(--color-text-muted); + font-family: var(--font-base); + font-size: 13px; + cursor: pointer; + transition: color 0.15s ease, border-color 0.15s ease; +} + +.expandBtn:hover { + color: var(--color-text); + border-color: var(--color-text-muted); +} + +/* ─── Diagram ─────────────────────────────────────────────────────── */ +.diagram { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; +} + +/* ─── Row ─────────────────────────────────────────────────────────── */ +.row { + width: 100%; + display: flex; + justify-content: center; +} + +/* ─── Box ─────────────────────────────────────────────────────────── */ +.box { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 28px 0; + border: 2px solid var(--color-border); + border-radius: 6px; + font-size: 20px; + font-weight: 400; + color: var(--color-text); + background: var(--color-surface); + width: 80%; +} + +.muted { + color: var(--color-text-muted); + margin-left: 8px; +} + +/* ─── Arrow down ──────────────────────────────────────────────────── */ +.arrowRow { + display: flex; + justify-content: center; + height: 48px; + align-items: center; +} + +.arrowDown { display: block; } + +/* ─── Control Plane ───────────────────────────────────────────────── */ +.controlPlane { + flex-direction: column; + gap: 14px; + padding: 32px 0; + border-color: var(--color-primary); + border-width: 2px; + width: 80%; +} + +.cpHeader { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; +} + +.cpTitle { + font-size: 24px; + font-weight: 700; + color: var(--color-text); +} + +.cpPills { + display: flex; + align-items: center; + gap: 12px; + flex-wrap: wrap; + justify-content: center; + font-size: 16px; + color: var(--color-text-muted); +} + +.sep { color: var(--color-border); } + +/* ─── Split arrows ────────────────────────────────────────────────── */ +.splitArrows { + width: 80%; + display: flex; + justify-content: space-around; + height: 48px; +} + +/* ─── Clusters ────────────────────────────────────────────────────── */ +.clustersRow { + display: flex; + align-items: center; + width: 80%; +} + +.clusterBox { + display: flex; + flex-direction: column; + align-items: center; + gap: 14px; + padding: 32px 0; + border: 2px solid var(--color-border); + border-radius: 6px; + font-size: 16px; + color: var(--color-text); + text-align: center; + line-height: 1.5; + flex: 1; + background: var(--color-surface); +} + +.arrowBi { display: block; flex-shrink: 0; } + +/* ─── Storage ─────────────────────────────────────────────────────── */ +.storageRow { + display: flex; + align-items: center; + gap: 20px; + text-align: left; +} + +.storageTitle { + font-size: 20px; + color: var(--color-text); +} + +.storageSub { + font-size: 15px; + color: var(--color-text-muted); + margin-top: 6px; +} + +/* ─── Divider ─────────────────────────────────────────────────────── */ +.divider { + width: 80%; + border: none; + border-top: 1px solid var(--color-border); + margin: 48px auto; +} + +/* ─── Features Grid ───────────────────────────────────────────────── */ +.featuresGrid { + width: 100%; + display: grid; + grid-template-columns: repeat(6, 1fr); + border: 2px solid var(--color-border); + border-radius: 8px; + overflow: hidden; +} + +.featureCard { + display: flex; + flex-direction: column; + gap: 12px; + padding: 32px 24px; + border-right: 2px solid var(--color-border); + transition: background 0.2s ease; +} + +.featureCard:last-child { border-right: none; } +.featureCard:hover { background: var(--color-surface); } + +.featureIcon { color: var(--color-text); } + +.featureLabel { + font-size: var(--fs-sm); + font-weight: var(--fw-bold); + color: var(--color-text); +} + +.featureDesc { + font-size: 16px; + color: var(--color-text-muted); + line-height: 1.5; +} + +/* ─── Lightbox overlay ────────────────────────────────────────────── */ +.lbOverlay { + position: fixed; + inset: 0; + z-index: 99998; + background: rgba(0, 0, 0, 0.88); + display: flex; + flex-direction: column; + animation: lbFadeIn 0.2s ease forwards; +} + +@keyframes lbFadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.lbCanvas { + flex: 1; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + cursor: grab; + user-select: none; + touch-action: none; +} + +.lbCanvas:active { cursor: grabbing; } + +.lbContent { + background: #ffffff; + border-radius: 12px; + padding: 48px 60px; + transform-origin: center center; + will-change: transform; +} + +/* ─── Lightbox controls ───────────────────────────────────────────── */ +.lbControls { + position: absolute; + bottom: 28px; + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + gap: 4px; + background: rgba(10, 10, 10, 0.7); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 100px; + padding: 6px 12px; + backdrop-filter: blur(12px); + z-index: 1; +} + +.lbBtn { + width: 36px; + height: 36px; + border-radius: 50%; + border: none; + background: transparent; + color: #ccc; + font-size: 20px; + font-weight: 300; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + transition: color 0.15s ease, background 0.15s ease; +} + +.lbBtn:hover { + color: #fff; + background: rgba(255, 255, 255, 0.1); +} + +.lbScale { + color: #888; + font-family: monospace; + font-size: 13px; + min-width: 46px; + text-align: center; +} + +.lbClose { + position: absolute; + top: 16px; + right: 16px; + width: 40px; + height: 40px; + border-radius: 50%; + border: 1px solid rgba(255, 255, 255, 0.15); + background: rgba(0, 0, 0, 0.5); + color: #ccc; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + backdrop-filter: blur(8px); + transition: color 0.15s ease, background 0.15s ease; + z-index: 2; +} + +.lbClose:hover { + color: #fff; + background: rgba(255, 255, 255, 0.12); +} + +/* ─── Responsive ──────────────────────────────────────────────────── */ +@media (max-width: 1100px) { + .inner { padding: 0 48px; } + .box, .controlPlane, .splitArrows, .clustersRow { width: 90%; } + .featuresGrid { grid-template-columns: repeat(3, 1fr); } + .featureCard:nth-child(3) { border-right: none; } + .featureCard:nth-child(n+4) { border-top: 2px solid var(--color-border); } +} + +@media (max-width: 768px) { + .inner { padding: 40px 24px; } + .section { min-height: auto; } + .box, .controlPlane, .splitArrows, .clustersRow { width: 100%; } + .box { font-size: 15px; padding: 20px 12px; } + .cpTitle { font-size: 18px; } + .cpPills { font-size: 13px; gap: 8px; } + .clusterBox { padding: 20px 8px; font-size: 13px; gap: 8px; } + .storageTitle { font-size: 15px; } + .storageSub { font-size: 13px; } + .featuresGrid { grid-template-columns: repeat(2, 1fr); } + .featureCard:nth-child(2n) { border-right: none; } + .featureCard:nth-child(3), + .featureCard:nth-child(4), + .featureCard:nth-child(5), + .featureCard:nth-child(6) { border-top: 2px solid var(--color-border); } + .featureCard:nth-child(3) { border-right: 1px solid var(--color-border); } + .featureCard:nth-child(5) { border-right: 1px solid var(--color-border); } + .featureDesc { font-size: 13px; } + .divider { margin: 32px auto; } + .lbContent { padding: 32px 24px; } +} + +@media (max-width: 480px) { + .inner { padding: 32px 16px; } + .featuresGrid { grid-template-columns: 1fr; } + .featureCard { border-right: none; border-bottom: 2px solid var(--color-border); } + .featureCard:last-child { border-bottom: none; } + .featureCard:nth-child(n) { border-top: none; border-right: none; } + .clustersRow { flex-direction: column; gap: 0; } + .arrowBi { transform: rotate(90deg); } + .splitArrows { display: none; } + .clusterBox { width: 100%; border-radius: 0; } + .clusterBox:first-child { border-radius: 6px 6px 0 0; } + .clusterBox:last-child { border-radius: 0 0 6px 6px; } +} diff --git a/src/components/CLISection/index.js b/src/components/CLISection/index.js new file mode 100644 index 0000000..293ed3b --- /dev/null +++ b/src/components/CLISection/index.js @@ -0,0 +1,486 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { createPortal } from 'react-dom'; +import styles from './styles.module.css'; + +/* ── Getting-started workflow (inline terminal) ─────────────────────── */ +const SEQUENCES = [ + { + command: '$ curl -fsSL https://autobase.tech/platform.yml \\\n --output ./docker-compose.platform.yml', + result: '✓ Downloaded', + detail: ' ./docker-compose.platform.yml', + }, + { + command: '$ docker compose \\\n -f docker-compose.platform.yml up -d', + result: '✓ Autobase Console started', + detail: ' http://your-server-address', + }, + { + command: '$ curl -fsSL https://autobase.tech/platform.ssl.yml \\\n --output ./docker-compose.platform.ssl.yml', + result: '✓ Downloaded', + detail: ' ./docker-compose.platform.ssl.yml', + }, + { + command: '$ docker compose \\\n -f docker-compose.platform.ssl.yml up -d', + result: '✓ Autobase Console started (SSL)', + detail: ' https://autobase.example.com', + }, +]; + +/* ── Getting-started workflow (fullscreen terminal — sequential) ────── */ +const FS_SEQUENCES = [ + { + command: '$ curl -fsSL https://autobase.tech/platform.yml \\\n --output ./docker-compose.platform.yml', + result: '✓ Downloaded', + detail: ' ./docker-compose.platform.yml', + }, + { + command: '$ echo "AUTH_TOKEN=secret-token" > .env', + result: '✓ Environment configured', + detail: ' .env', + }, + { + command: '$ docker compose \\\n -f docker-compose.platform.yml up -d', + result: '✓ Autobase Console started', + detail: ' http://your-server-address', + }, + { + command: '$ curl -fsSL https://autobase.tech/platform.ssl.yml \\\n --output ./docker-compose.platform.ssl.yml', + result: '✓ Downloaded', + detail: ' ./docker-compose.platform.ssl.yml', + }, + { + command: '$ echo "DOMAIN=autobase.example.com\nACME_EMAIL=admin@example.com\nAUTH_TOKEN=secret-token" > .env', + result: '✓ SSL environment configured', + detail: ' .env', + }, + { + command: '$ docker compose \\\n -f docker-compose.platform.ssl.yml up -d', + result: '✓ Autobase Console started (SSL)', + detail: ' https://autobase.example.com', + }, + { + command: '$ autobase upgrade \\\n --cluster production \\\n --from 15 --to 16', + result: '✓ Upgrade complete — zero downtime', + detail: ' primary: 16.3 replicas: 16.3', + }, + { + command: '=# CHECKPOINT;', + result: 'CHECKPOINT', + detail: ' wal_lsn: 0/3B00000 duration: 0.21s', + }, +]; + +const CLEAR_CMD = '\n$ \\! clear'; +const CHAR_SPEED = 28; +const FS_SPEED = 12; // faster for fullscreen +const CLS_SPEED = 90; +const HOLD_MS = 2600; +const FLASH_MS = 420; +const FS_HOLD_MS = 1800; // shorter hold in fullscreen before next command + + +/* ── Code-rain canvas for the overlay background ──────────────────── */ +const RAIN_CHARS = ( + '0123456789ABCDEFabcdef' + + 'SELECTFROMWHEREINSERTUPDATECREATEINDEXREPLICACLUSTER' + + 'WALPSQLNODESHARDLSNPGBASEAUTOBASE\\$=>_{}[]|;:,.' +).split('').filter((c, i, a) => a.indexOf(c) === i); + +function MatrixCanvas() { + const ref = useRef(null); + + useEffect(() => { + const canvas = ref.current; + if (!canvas) return; + const ctx = canvas.getContext('2d'); + + const FONT_SIZE = 14; + const LINE_H = 20; + + let width, height, cols, drops, speeds, brightChance, frames; + + function init() { + width = window.innerWidth; + height = window.innerHeight; + canvas.width = width; + canvas.height = height; + cols = Math.floor(width / FONT_SIZE); + drops = Array.from({ length: cols }, () => Math.random() * -(height / LINE_H)); + speeds = Array.from({ length: cols }, () => 0.18 + Math.random() * 0.38); + brightChance = Array.from({ length: cols }, () => Math.random()); + frames = 0; + ctx.fillStyle = '#040404'; + ctx.fillRect(0, 0, width, height); + } + + init(); + window.addEventListener('resize', init); + + let raf; + function draw() { + frames++; + + // Slowly fade existing pixels (trail effect) + ctx.fillStyle = 'rgba(4, 4, 4, 0.055)'; + ctx.fillRect(0, 0, width, height); + + ctx.font = `${FONT_SIZE}px "Courier New", monospace`; + + for (let i = 0; i < cols; i++) { + const y = Math.floor(drops[i]) * LINE_H; + if (y < -LINE_H || y > height + LINE_H) { + drops[i] += speeds[i]; + continue; + } + + const char = RAIN_CHARS[Math.floor(Math.random() * RAIN_CHARS.length)]; + const roll = Math.random(); + + if (roll < 0.006) { + // Orange spark — very rare + ctx.fillStyle = `rgba(255, 100, 30, ${0.55 + Math.random() * 0.3})`; + } else if (roll < 0.025 && brightChance[i] > 0.7) { + // Bright green-white head + ctx.fillStyle = `rgba(200, 230, 200, ${0.18 + Math.random() * 0.12})`; + } else if (roll < 0.10) { + // Mid green + ctx.fillStyle = `rgba(50, 130, 70, ${0.06 + Math.random() * 0.05})`; + } else { + // Dim: almost invisible + ctx.fillStyle = `rgba(20, 55, 30, ${0.025 + Math.random() * 0.025})`; + } + + ctx.fillText(char, i * FONT_SIZE, y); + + drops[i] += speeds[i]; + + // Reset column when it exits the bottom + if (y > height && Math.random() > 0.975) { + drops[i] = Math.random() * -(height / LINE_H * 0.5); + speeds[i] = 0.18 + Math.random() * 0.38; + brightChance[i] = Math.random(); + } + } + + raf = requestAnimationFrame(draw); + } + + draw(); + return () => { + cancelAnimationFrame(raf); + window.removeEventListener('resize', init); + }; + }, []); + + return ( + + ); +} + +function ExpandIcon() { + return ( + + ); +} +function CollapseIcon() { + return ( + + ); +} + +/* ── Inline terminal body (cycles SEQUENCES) ──────────────────────── */ +function InlineBody({ typed, clsTyped, phase, seq }) { + const showResult = phase === 'showing' || phase === 'typing_cls' || phase === 'flash'; + const showCls = phase === 'typing_cls' || phase === 'flash'; + const isFlashing = phase === 'flash'; + const displayText = typed.startsWith('$ ') ? typed.slice(2) : typed; + + return ( +
+      $
+      {displayText}
+      {phase === 'typing' && _}
+      {showResult && (
+        <>
+          {'\n\n'}
+          {seq.result}
+          {'\n'}
+          {seq.detail}
+        
+      )}
+      {showCls && (
+        <>
+          {'\n'}
+          {clsTyped}
+          {phase === 'typing_cls' && clsTyped.length < CLEAR_CMD.length && (
+            _
+          )}
+        
+      )}
+    
+ ); +} + +/* ── Fullscreen terminal body (independent random cycling) ────────── */ +function FullscreenBody({ fsSeq, fsTyped, fsPhase }) { + const showResult = fsPhase === 'showing' || fsPhase === 'clearing'; + const isClearing = fsPhase === 'clearing'; + + return ( +
+ {/* Scrolling terminal area */} +
+        {fsSeq.command.startsWith('=# ') ? (
+          <>
+            =# 
+            {fsSeq.command.slice(3)}
+          
+        ) : (
+          <>
+            $
+            {fsSeq.command.startsWith('$ ') ? fsSeq.command.slice(2) : fsSeq.command}
+          
+        )}
+        {fsPhase === 'typing' && _}
+        {showResult && (
+          <>
+            {'\n\n'}
+            {fsSeq.result}
+            {'\n'}
+            {fsSeq.detail}
+          
+        )}
+      
+ + {/* CTA menu — fixed below terminal, always visible */} +
+ +
+ ); +} + +export default function CLISection() { + /* ── Inline cycling state ── */ + const [seqIdx, setSeqIdx] = useState(0); + const [typed, setTyped] = useState(''); + const [clsTyped, setClsTyped] = useState(''); + const [phase, setPhase] = useState('typing'); + const timer = useRef(null); + + /* ── Fullscreen state ── */ + const [fullscreen, setFullscreen] = useState(false); + const [fsClosing, setFsClosing] = useState(false); + const [fsIdx, setFsIdx] = useState(0); + const [fsTyped, setFsTyped] = useState(''); + const [fsPhase, setFsPhase] = useState('idle'); // idle | typing | showing | clearing + const fsTimer = useRef(null); + + const seq = SEQUENCES[seqIdx]; + const fsSeq = FS_SEQUENCES[fsIdx]; + + /* ── Inline typewriter ── */ + useEffect(() => { + if (phase !== 'typing') return; + clearTimeout(timer.current); + const cmd = seq.command; + if (typed.length < cmd.length) { + timer.current = setTimeout(() => setTyped(cmd.slice(0, typed.length + 1)), CHAR_SPEED); + } else { + timer.current = setTimeout(() => setPhase('showing'), 300); + } + return () => clearTimeout(timer.current); + }, [phase, typed, seq.command]); + + useEffect(() => { + if (phase !== 'showing') return; + timer.current = setTimeout(() => { setClsTyped(''); setPhase('typing_cls'); }, HOLD_MS); + return () => clearTimeout(timer.current); + }, [phase]); + + useEffect(() => { + if (phase !== 'typing_cls') return; + clearTimeout(timer.current); + if (clsTyped.length < CLEAR_CMD.length) { + timer.current = setTimeout(() => setClsTyped(CLEAR_CMD.slice(0, clsTyped.length + 1)), CLS_SPEED); + } else { + timer.current = setTimeout(() => setPhase('flash'), 180); + } + return () => clearTimeout(timer.current); + }, [phase, clsTyped]); + + useEffect(() => { + if (phase !== 'flash') return; + timer.current = setTimeout(() => { + setTyped(''); setClsTyped(''); + setSeqIdx(i => (i + 1) % SEQUENCES.length); + setPhase('typing'); + }, FLASH_MS); + return () => clearTimeout(timer.current); + }, [phase]); + + /* ── Fullscreen: start fresh on open ── */ + useEffect(() => { + if (fullscreen) { + setFsIdx(0); + setFsTyped(''); + setFsPhase('typing'); + } else { + clearTimeout(fsTimer.current); + setFsPhase('idle'); + } + }, [fullscreen]); + + /* ── Fullscreen typewriter ── */ + useEffect(() => { + if (fsPhase !== 'typing') return; + clearTimeout(fsTimer.current); + const target = fsSeq.command; + if (fsTyped.length < target.length) { + fsTimer.current = setTimeout(() => setFsTyped(target.slice(0, fsTyped.length + 1)), FS_SPEED); + } else { + fsTimer.current = setTimeout(() => setFsPhase('showing'), 240); + } + return () => clearTimeout(fsTimer.current); + }, [fsPhase, fsTyped, fsSeq.command]); + + /* ── Fullscreen hold → clear → next ── */ + useEffect(() => { + if (fsPhase !== 'showing') return; + fsTimer.current = setTimeout(() => setFsPhase('clearing'), FS_HOLD_MS); + return () => clearTimeout(fsTimer.current); + }, [fsPhase]); + + useEffect(() => { + if (fsPhase !== 'clearing') return; + fsTimer.current = setTimeout(() => { + setFsTyped(''); + setFsIdx(i => (i + 1) % FS_SEQUENCES.length); + setFsPhase('typing'); + }, FLASH_MS); + return () => clearTimeout(fsTimer.current); + }, [fsPhase]); + + /* ── Close with exit animation ── */ + const closeFullscreen = () => { + setFsClosing(true); + setTimeout(() => { + setFsClosing(false); + setFullscreen(false); + }, 280); + }; + + /* ── Escape key ── */ + useEffect(() => { + if (!fullscreen) return; + const onKey = (e) => { if (e.key === 'Escape') closeFullscreen(); }; + document.addEventListener('keydown', onKey); + return () => document.removeEventListener('keydown', onKey); + }, [fullscreen]); + + const bar = (isFullscreen) => ( +
+ + +
+ ); + + return ( +
+ + + {fullscreen && typeof document !== 'undefined' && createPortal( +
+ +
e.stopPropagation()} + > + {bar(true)} + +
+
, + document.body + )} +
+ ); +} diff --git a/src/components/CLISection/styles.module.css b/src/components/CLISection/styles.module.css new file mode 100644 index 0000000..1182a1c --- /dev/null +++ b/src/components/CLISection/styles.module.css @@ -0,0 +1,421 @@ +/* ─── Section ─────────────────────────────────────────────────────── */ +.section { + background: var(--color-bg); + padding: var(--section-pt) 0 var(--section-pb); +} + +.inner { + max-width: 1200px; + margin: 0 auto; + padding: 0 80px; +} + +/* ─── Two-column layout ───────────────────────────────────────────── */ +.layout { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 40px; + align-items: center; +} + +/* ─── Terminal (inline) ───────────────────────────────────────────── */ +.terminal { + background: #0d0d0d; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 8px 40px rgba(0, 0, 0, 0.15); +} + +.terminalBar { + display: flex; + align-items: center; + gap: 8px; + padding: 14px 18px; + background: #1a1a1a; +} + +.dot { + width: 12px; + height: 12px; + border-radius: 50%; + background: #3a3a3a; +} + +.expandBtn { + margin-left: auto; + background: none; + border: none; + color: #555; + cursor: pointer; + padding: 4px 6px; + border-radius: 4px; + display: flex; + align-items: center; + transition: color 0.15s ease, background 0.15s ease; +} + +.expandBtn:hover { + color: #bbb; + background: rgba(255, 255, 255, 0.07); +} + +/* ─── Terminal code block ─────────────────────────────────────────── */ +.terminalCode { + font-family: 'Courier New', Courier, monospace; + font-size: 14px; + line-height: 1.7; + color: #c8c8c8; + padding: 24px 28px; + margin: 0; + white-space: pre; + overflow-x: auto; + background: transparent; + border: none; + min-height: 220px; +} + +.terminalCodeFull { + font-family: 'Courier New', Courier, monospace; + font-size: 15px; + line-height: 1.75; + color: #c8c8c8; + padding: 36px 44px 28px; + margin: 0; + white-space: pre; + overflow-x: auto; + background: transparent; + border: none; + min-height: 160px; +} + +.promptPg { + color: #569cd6; + font-weight: bold; +} + +/* CRT phosphor flash */ +.terminalFlash { + animation: crtClear 0.42s ease-in forwards; +} + +@keyframes crtClear { + 0% { opacity: 1; filter: brightness(1) blur(0px); } + 25% { opacity: 1; filter: brightness(3.5) blur(1px); } + 55% { opacity: 0.15; filter: brightness(0.4) blur(2px); } + 100% { opacity: 0; filter: brightness(0) blur(0px); } +} + +.prompt { + color: var(--color-primary); + font-weight: bold; +} + +.caret { + display: inline-block; + color: #c8c8c8; + animation: caretBlink 1s step-end infinite; + user-select: none; +} + +@keyframes caretBlink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + +.success { + color: #4caf50; + font-weight: bold; +} + +.dimmed { + color: #666666; +} + +/* ─── Fullscreen layout ───────────────────────────────────────────── */ +.fsLayout { + display: flex; + flex-direction: column; + height: 100%; +} + +/* ─── Fullscreen CTA menu ─────────────────────────────────────────── */ +.ctaMenu { + display: flex; + flex-direction: column; + gap: 4px; + padding: 0 44px 36px; + font-family: 'Courier New', Courier, monospace; + font-size: 15px; + line-height: 1.75; +} + +.menuDivider { + height: 1px; + background: #1e1e1e; + margin-bottom: 20px; +} + +.menuItem { + display: grid; + grid-template-columns: 24px 1fr; + grid-template-rows: auto auto; + gap: 2px 10px; + padding: 10px 12px; + border-radius: 6px; + text-decoration: none; + color: inherit; + transition: background 0.15s ease; + margin-bottom: 4px; +} + +.menuItem:hover { + background: rgba(255, 255, 255, 0.04); + text-decoration: none; +} + +.menuItem:hover .menuPrompt { color: #ff7043; } +.menuItem:hover .menuTitle { color: #ffffff; } +.menuItem:hover .menuSub { color: #888888; } + +.menuPrompt { + grid-column: 1; + grid-row: 1 / 3; + align-self: center; + color: var(--color-primary); + font-weight: bold; + font-size: 17px; + transition: color 0.15s ease; +} + +.menuTitle { + grid-column: 2; + grid-row: 1; + color: #d0d0d0; + font-weight: bold; + letter-spacing: 0.3px; + transition: color 0.15s ease; +} + +.menuSub { + grid-column: 2; + grid-row: 2; + color: #444; + font-size: 13px; + transition: color 0.15s ease; +} + +/* ─── CTAs column (original) ──────────────────────────────────────── */ +.ctas { + display: flex; + flex-direction: column; + gap: 16px; +} + +.ctaPrimary, +.ctaSecondary { + display: flex; + align-items: center; + gap: 20px; + padding: 24px 28px; + border-radius: 6px; + text-decoration: none; + transition: opacity 0.2s ease, transform 0.15s ease; +} + +.ctaPrimary:hover, +.ctaSecondary:hover { + opacity: 0.85; + transform: translateY(-1px); + text-decoration: none; +} + +.ctaPrimary { + border: 1.5px solid var(--color-primary); + background: transparent; +} + +.ctaSecondary { + border: 1.5px solid var(--color-border); + outline: 1.5px solid transparent; + outline-offset: 0px; + background: transparent; + transform-style: preserve-3d; + transform-origin: center top; + transition: border-color 0.18s ease, outline-color 0.18s ease, + background 0.18s ease, + transform 0.18s cubic-bezier(0.25, 0.46, 0.45, 0.94), + box-shadow 0.18s ease; +} + +.ctaSecondary:hover { + border-color: var(--color-primary); + outline-color: var(--color-primary); + background: var(--color-surface); + opacity: 1; + transform: perspective(600px) rotateX(-7deg) translateY(4px) scaleY(0.97); + box-shadow: 0 4px 0 rgba(0,0,0,0.07), inset 0 2px 4px rgba(0,0,0,0.05); +} + +.ctaArrow { + font-family: monospace; + font-size: 20px; + font-weight: bold; + color: var(--color-primary); + flex-shrink: 0; +} + +.ctaTitle { + font-family: var(--font-base); + font-size: var(--fs-body); + font-weight: var(--fw-bold); + color: var(--color-text); + text-transform: uppercase; + letter-spacing: 0.3px; +} + +.ctaSubtitle { + font-family: var(--font-base); + font-size: var(--fs-sm); + font-weight: var(--fw-regular); + color: var(--color-text-muted); + margin-top: 4px; +} + +/* ─── Fullscreen overlay ──────────────────────────────────────────── */ +.overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 99999; + /* Canvas handles darkening; keep bg as pure black base */ + background: #040404; + display: flex; + align-items: center; + justify-content: center; + animation: overlayIn 0.25s ease forwards; +} + +.overlayClosing { + animation: overlayOut 0.28s ease forwards; +} + +@keyframes overlayIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes overlayOut { + from { opacity: 1; } + to { opacity: 0; } +} + +.overlayTerminal { + position: relative; + z-index: 1; + background: rgba(10, 10, 10, 0.92); + backdrop-filter: blur(2px); + border-radius: 12px; + box-shadow: + 0 0 0 1px #2a2a2a, + 0 40px 100px rgba(0, 0, 0, 0.9), + 0 0 120px rgba(255, 87, 34, 0.06), + 0 0 40px rgba(30, 100, 50, 0.08); + width: min(860px, 90vw); + height: min(560px, 85vh); + overflow-y: auto; + animation: windowOpen 0.32s cubic-bezier(0.34, 1.46, 0.64, 1) forwards; + transform-origin: center center; +} + +.overlayTerminalClosing { + animation: windowClose 0.22s cubic-bezier(0.55, 0, 1, 0.45) forwards; +} + +@keyframes windowClose { + from { + opacity: 1; + transform: scale(1) translateY(0); + filter: brightness(1); + } + to { + opacity: 0; + transform: scale(0.88) translateY(18px); + filter: brightness(0.4); + } +} + +/* Scanline sweep on open */ +.overlayTerminal::before { + content: ''; + position: absolute; + inset: 0; + height: 3px; + background: linear-gradient(to bottom, + transparent 0%, + rgba(150, 255, 180, 0.18) 50%, + transparent 100% + ); + animation: scanline 0.7s ease-in 0.08s forwards; + pointer-events: none; + z-index: 1; +} + +@keyframes scanline { + 0% { top: 0; opacity: 0.7; } + 85% { top: 100%; opacity: 0.3; } + 100% { top: 100%; opacity: 0; } +} + +@keyframes windowOpen { + from { + opacity: 0; + transform: scale(0.84) translateY(28px); + filter: brightness(0.6); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + filter: brightness(1); + } +} + + +/* ─── Responsive ──────────────────────────────────────────────────── */ +@media (max-width: 900px) { + .inner { padding: 0 48px; } + .layout { grid-template-columns: 1fr; } + .ctas { flex-direction: row; flex-wrap: wrap; gap: 12px; } + .ctaPrimary, .ctaSecondary { flex: 1; min-width: 200px; padding: 20px 20px; } +} + +@media (max-width: 600px) { + .inner { padding: 0 20px; } + .terminalCode { font-size: 12px; padding: 16px; min-height: 160px; } + .ctas { flex-direction: column; } + .ctaPrimary, .ctaSecondary { width: 100%; } +} + +/* ─── Fullscreen overlay — mobile ─────────────────────────────────── */ +@media (max-width: 768px) { + .overlayTerminal { + width: 96vw; + height: 90vh; + border-radius: 8px; + } + .terminalCodeFull { + font-size: 12px; + padding: 24px 20px 16px; + min-height: 120px; + } + .ctaMenu { + padding: 0 20px 24px; + font-size: 13px; + } + .menuItem { + padding: 8px 8px; + grid-template-columns: 20px 1fr; + } + .menuPrompt { font-size: 15px; } +} diff --git a/src/components/ClickEffect/index.js b/src/components/ClickEffect/index.js new file mode 100644 index 0000000..4acbc46 --- /dev/null +++ b/src/components/ClickEffect/index.js @@ -0,0 +1,108 @@ +import React, { useEffect, useRef } from 'react'; + +// Digits pool — mix of decimal + hex for a pg/terminal data feel +const DIGITS = '0123456789ABCDEF01234567890123456789'; + +// Weighted color pool — mostly dark, rare orange spark +const COLORS = [ + { color: '#111111', weight: 5 }, + { color: '#555555', weight: 3 }, + { color: '#999999', weight: 2 }, + { color: '#ff5722', weight: 1 }, +]; +const TOTAL_WEIGHT = COLORS.reduce((s, c) => s + c.weight, 0); + +function pickColor() { + let r = Math.random() * TOTAL_WEIGHT; + for (const c of COLORS) { + r -= c.weight; + if (r <= 0) return c.color; + } + return COLORS[0].color; +} + +function pickDigit() { + return DIGITS[Math.floor(Math.random() * DIGITS.length)]; +} + +export default function ClickEffect() { + const canvasRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext('2d'); + let particles = []; + let raf; + + function resize() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + } + resize(); + window.addEventListener('resize', resize); + + function spawn(x, y) { + const count = 8 + Math.floor(Math.random() * 7); + for (let i = 0; i < count; i++) { + const angle = Math.random() * Math.PI * 2; + const speed = 1.2 + Math.random() * 4; + const fontSize = 10 + Math.floor(Math.random() * 8); // 10–17px + particles.push({ + x, y, + vx: Math.cos(angle) * speed, + vy: Math.sin(angle) * speed - 2, // bias upward + alpha: 0.9 + Math.random() * 0.1, + decay: 0.025 + Math.random() * 0.025, + color: pickColor(), + digit: pickDigit(), + fontSize, + font: `${fontSize}px "Courier New", monospace`, + }); + } + } + + function animate() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + particles = particles.filter(p => p.alpha > 0); + for (const p of particles) { + p.x += p.vx; + p.y += p.vy; + p.vy += 0.18; // gravity + p.vx *= 0.97; // drag + p.alpha -= p.decay; + + ctx.globalAlpha = Math.max(0, p.alpha); + ctx.fillStyle = p.color; + ctx.font = p.font; + ctx.fillText(p.digit, Math.round(p.x), Math.round(p.y)); + } + ctx.globalAlpha = 1; + raf = requestAnimationFrame(animate); + } + animate(); + + function onClick(e) { spawn(e.clientX, e.clientY); } + document.addEventListener('click', onClick); + + return () => { + cancelAnimationFrame(raf); + document.removeEventListener('click', onClick); + window.removeEventListener('resize', resize); + }; + }, []); + + return ( + + ); +} diff --git a/src/components/ContactSection/index.js b/src/components/ContactSection/index.js new file mode 100644 index 0000000..2d8f483 --- /dev/null +++ b/src/components/ContactSection/index.js @@ -0,0 +1,96 @@ +import React from 'react'; +import styles from './styles.module.css'; + +function TelegramIcon() { + return ( + + ); +} + +function YouTubeIcon() { + return ( + + ); +} + +function GitHubIcon() { + return ( + + ); +} + +const columns = [ + { + title: 'Contact', + subtitle: "We'd love to hear from you.", + links: [ + { label: '@autobase_tech', href: 'https://x.com/autobase_tech', prefix: 'X' }, + { label: 'info@autobase.tech', href: 'mailto:info@autobase.tech', prefix: '@' }, + { label: '@autobase_tech', href: 'https://t.me/autobase_tech', prefix: }, + { label: 'youtube.com/@Autobase', href: 'https://youtube.com/@Autobase', prefix: }, + { label: 'github.com/autobasehq', href: 'https://github.com/autobase-tech/autobase', prefix: }, + ], + }, + { + title: 'Docs', + subtitle: 'Everything you need to know.', + links: [ + { label: '→ docs.autobase.tech', href: '/docs' }, + ], + }, + { + title: 'Support', + subtitle: 'Get help and resources.', + links: [ + { label: '→ autobase.tech/support', href: '/support' }, + ], + }, +]; + +export default function ContactSection() { + return ( +
+
+
+ {columns.map((col) => ( +
+

{col.title}

+ {col.subtitle &&

{col.subtitle}

} + +
+ ))} +
+
+
+ ); +} diff --git a/src/components/ContactSection/styles.module.css b/src/components/ContactSection/styles.module.css new file mode 100644 index 0000000..3ac0008 --- /dev/null +++ b/src/components/ContactSection/styles.module.css @@ -0,0 +1,103 @@ +/* ─── Section ─────────────────────────────────────────────────────── */ +.section { + background: + linear-gradient(to right, transparent 10%, var(--color-border) 10%, var(--color-border) 90%, transparent 90%) no-repeat 0 0 / 100% 1px, + var(--color-bg); + padding: 16px 0 var(--section-pb); + margin-top: 0; +} + +.inner { + max-width: 1200px; + margin: 0 auto; + padding: 0 80px; +} + +/* ─── Grid ────────────────────────────────────────────────────────── */ +.grid { + display: grid; + grid-template-columns: 2fr 1fr 1fr; + gap: 60px; +} + +/* ─── Column ──────────────────────────────────────────────────────── */ +.column { + display: flex; + flex-direction: column; + gap: 16px; +} + +.columnTitle { + font-family: var(--font-base); + font-size: var(--fs-body-lg); + font-weight: var(--fw-semibold); + color: var(--color-text); + margin: 0; +} + +.columnSubtitle { + font-family: var(--font-base); + font-size: var(--fs-sm); + font-weight: var(--fw-regular); + color: var(--color-text-muted); + margin: -8px 0 0; + line-height: 1.5; +} + +/* ─── Link list ───────────────────────────────────────────────────── */ +.linkList { + list-style: none; + padding: 0; + margin: 1rem 0 0; + display: flex; + flex-direction: column; + gap: 24px; +} + +.link { + display: flex; + align-items: center; + gap: 10px; + font-family: var(--font-base); + font-size: var(--fs-sm); + font-weight: var(--fw-regular); + color: var(--color-text-muted); + text-decoration: none; + transition: color 0.15s ease; +} + +.link:hover { + color: var(--color-text); + text-decoration: none; +} + +.prefix { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 36px; + height: 36px; + padding: 0 6px; + border-radius: 4px; + font-size: 13px; + font-weight: var(--fw-bold); + color: var(--color-primary); + flex-shrink: 0; +} + +/* ─── Responsive ──────────────────────────────────────────────────── */ +@media (max-width: 900px) { + .inner { padding: 0 48px; } + .grid { grid-template-columns: 1fr 1fr; gap: 32px; } +} + +@media (max-width: 600px) { + .inner { padding: 0 20px; } + .grid { grid-template-columns: 1fr; gap: 28px; } + .section { padding: 32px 0 var(--section-pb); } +} + +@media (max-width: 480px) { + .linkList { gap: 18px; } + .prefix { min-width: 30px; height: 30px; } +} diff --git a/src/components/FeaturesGridSection/index.js b/src/components/FeaturesGridSection/index.js new file mode 100644 index 0000000..cd18afe --- /dev/null +++ b/src/components/FeaturesGridSection/index.js @@ -0,0 +1,95 @@ +import React from 'react'; +import styles from './styles.module.css'; + +const features = [ + { + icon: ( + + ), + label: 'HA', + title: 'High Availability', + description: 'Automatic High Availability', + }, + { + icon: ( + + ), + label: 'Failover', + title: 'Failover', + description: 'Automatic Failover & Self-Healing', + }, + { + icon: ( + + ), + label: 'Backups', + title: 'Backups', + description: 'Continuous Backups', + }, + { + icon: ( + + ), + label: 'PITR', + title: 'PITR', + description: 'Point-in-Time Recovery', + }, + { + icon: ( + + ), + label: 'Monitoring', + title: 'Monitoring', + description: 'Built-in Metrics & Alerting', + }, + { + icon: ( + + ), + label: 'Upgrades', + title: 'Upgrades', + description: 'Zero-Downtime Upgrades', + }, +]; + +export default function FeaturesGridSection() { + return ( +
+
+
+ {features.map((f) => ( +
+
{f.icon}
+
{f.label}
+
{f.description}
+
+ ))} +
+
+
+ ); +} diff --git a/src/components/FeaturesGridSection/styles.module.css b/src/components/FeaturesGridSection/styles.module.css new file mode 100644 index 0000000..a12fb70 --- /dev/null +++ b/src/components/FeaturesGridSection/styles.module.css @@ -0,0 +1,93 @@ +/* ─── Section ─────────────────────────────────────────────────────── */ +.section { + background: + linear-gradient(to right, transparent 10%, var(--color-border) 10%, var(--color-border) 90%, transparent 90%) no-repeat 0 0 / 100% 1px, + var(--color-bg); + padding: var(--section-pt) 0 var(--section-pb); +} + +.inner { + max-width: 1200px; + margin: 0 auto; + padding: 0 200px; +} + +/* ─── Grid ────────────────────────────────────────────────────────── */ +.grid { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 0; + border: 1px solid var(--color-border); + border-radius: 8px; + overflow: hidden; +} + +/* ─── Card ────────────────────────────────────────────────────────── */ +.card { + display: flex; + flex-direction: column; + gap: 12px; + padding: 32px 24px; + border-right: 1px solid var(--color-border); + transition: background 0.2s ease; +} + +.card:last-child { + border-right: none; +} + +.card:hover { + background: var(--color-surface); +} + +/* ─── Icon ────────────────────────────────────────────────────────── */ +.icon { + color: var(--color-text); + display: flex; + align-items: center; +} + +/* ─── Label ───────────────────────────────────────────────────────── */ +.label { + font-family: var(--font-base); + font-size: var(--fs-sm); + font-weight: var(--fw-bold); + color: var(--color-text); +} + +/* ─── Description ─────────────────────────────────────────────────── */ +.description { + font-family: var(--font-base); + font-size: 13px; + font-weight: var(--fw-regular); + color: var(--color-text-muted); + line-height: 1.5; +} + +/* ─── Responsive ──────────────────────────────────────────────────── */ +@media (max-width: 1200px) { + .inner { padding: 0 80px; } +} + +@media (max-width: 1100px) { + .inner { padding: 0 48px; } + .grid { grid-template-columns: repeat(3, 1fr); } + .card:nth-child(3) { border-right: none; } + .card:nth-child(n+4) { border-top: 1px solid var(--color-border); } +} + +@media (max-width: 768px) { + .inner { padding: 0 20px; } + .grid { grid-template-columns: repeat(2, 1fr); } + .card:nth-child(3) { border-right: 1px solid var(--color-border); } + .card:nth-child(2n) { border-right: none; } + .card:nth-child(n+3) { border-top: 1px solid var(--color-border); } +} + +@media (max-width: 480px) { + .inner { padding: 0 16px; } + .grid { grid-template-columns: 1fr; } + .card { border-right: none; border-top: none; border-bottom: 1px solid var(--color-border); } + .card:last-child { border-bottom: none; } + .card:nth-child(n) { border-right: none; border-top: none; } +} diff --git a/src/components/HeroSection/index.js b/src/components/HeroSection/index.js index d08bfb5..9789aae 100644 --- a/src/components/HeroSection/index.js +++ b/src/components/HeroSection/index.js @@ -1,126 +1,69 @@ -import React, { useEffect, useRef, useState } from 'react'; -import Link from '@docusaurus/Link'; +import React, { useState, useEffect } from 'react'; import styles from './styles.module.css'; -const TYPEWRITER_TEXT = 'DBaaS Platform'; -const CHAR_SPEED = 58; +const LINE1 = 'POSTGRESQL'; +const LINE2 = 'CONTROL PLANE'; +const SUBTEXT = 'Self-Hosted DBaaS'; +const SPEED = 68; // ms per character +const H1_TOTAL = LINE1.length + LINE2.length; // 23 chars → ~1.56s +const SUB_DELAY = H1_TOTAL * SPEED + 200; // starts after headline + 0.2s pause export default function HeroSection() { - const postgresRef = useRef(null); - const bodyRef = useRef(null); - const [typed, setTyped] = useState(''); - const [cursorVisible, setCursorVisible] = useState(false); + const [h1Count, setH1Count] = useState(0); + const [subCount, setSubCount] = useState(0); + const [subStarted, setSubStarted] = useState(false); + // headline typing (left → right) useEffect(() => { - let mounted = true; + if (h1Count >= H1_TOTAL) return; + const id = setTimeout(() => setH1Count(n => n + 1), SPEED); + return () => clearTimeout(id); + }, [h1Count]); - import('gsap').then(({ gsap }) => { - if (!mounted) return; + // subheading delay trigger + useEffect(() => { + const id = setTimeout(() => setSubStarted(true), SUB_DELAY); + return () => clearTimeout(id); + }, []); - // 1. "PostgreSQL." — scale + opacity entrance - gsap.fromTo( - postgresRef.current, - { opacity: 0, scale: 0.88, y: 10 }, - { - opacity: 1, - scale: 1, - y: 0, - duration: 0.95, - ease: 'expo.out', - onComplete() { - if (!mounted) return; - setCursorVisible(true); - let i = 0; - const tick = setInterval(() => { - if (!mounted) { clearInterval(tick); return; } - i++; - setTyped(TYPEWRITER_TEXT.slice(0, i)); - if (i >= TYPEWRITER_TEXT.length) { - clearInterval(tick); - setTimeout(() => { if (mounted) setCursorVisible(false); }, 800); - } - }, CHAR_SPEED); - }, - } - ); + // subheading typing (right → left: rightmost chars appear first) + useEffect(() => { + if (!subStarted || subCount >= SUBTEXT.length) return; + const id = setTimeout(() => setSubCount(n => n + 1), SPEED); + return () => clearTimeout(id); + }, [subStarted, subCount]); - // 2. Body + CTAs fade up slightly after - gsap.fromTo( - bodyRef.current, - { opacity: 0, y: 18 }, - { opacity: 1, y: 0, duration: 0.9, ease: 'expo.out', delay: 0.55 } - ); - }); + const c1 = Math.min(h1Count, LINE1.length); + const c2 = Math.max(0, h1Count - LINE1.length); + const onLine1 = h1Count < LINE1.length; - return () => { mounted = false; }; - }, []); + // reverse: slice from the right, so last chars appear first + const subDisplayed = SUBTEXT.slice(SUBTEXT.length - subCount); + const subDone = subCount >= SUBTEXT.length; return ( -
- - +
-
+

+ + {LINE1.slice(0, c1)} + {onLine1 && _} + + + {LINE2.slice(0, c2)} + {!onLine1 && _} + +

- {/* Text column */} -
-
-

- - PostgreSQL - - - {typed} - {cursorVisible && ( - - )} - -

- -
-

- Get the managed database experience inside your own infrastructure. - Deploy, operate, scale and manage PostgreSQL with one unified platform and full infrastructure control. -

-
- - Get started - - - Chat on Telegram - - -
-
-
-
- - {/* Image column */} -
- PostgreSQL server illustration -
- -
+

+ {subStarted && ( + <> + {subDisplayed} + {!subDone && _} + + )} +

- - -
); } diff --git a/src/components/HeroSection/styles.module.css b/src/components/HeroSection/styles.module.css index 2d7d547..b45f320 100644 --- a/src/components/HeroSection/styles.module.css +++ b/src/components/HeroSection/styles.module.css @@ -1,236 +1,66 @@ -/* ─── Keyframes ───────────────────────────────────────────────────── */ -@keyframes heroFadeIn { - from { opacity: 0; transform: translateY(24px); } - to { opacity: 1; transform: translateY(0); } +/* ─── Section ─────────────────────────────────────────────────────── */ +.hero { + background: var(--color-bg); + border-bottom: 1px solid var(--color-border); } -@keyframes cursorBlink { - 0%, 49% { opacity: 1; } - 50%, 100% { opacity: 0; } -} - -/* ─── Section shell ───────────────────────────────────────────────── */ -.heroSection { - position: relative; - overflow: hidden; - border-bottom-left-radius: 48px; - border-bottom-right-radius: 48px; - background: linear-gradient(98.84deg, #000000 2.84%, #290a00 42.32%, #1a0600 95.10%); -} - -/* ─── Background texture ──────────────────────────────────────────── */ -.bgTexture { - position: absolute; - bottom: -288px; - left: 0; - width: 1600px; - height: 1600px; - opacity: 0.08; - pointer-events: none; - object-fit: cover; - z-index: 0; -} - -/* ─── Max-width centering wrapper (Bootstrap row lives inside) ────── */ .inner { - max-width: 1540px; + width: 100%; + max-width: 1200px; margin: 0 auto; - padding: 0 160px; - position: relative; - z-index: 1; -} - -/* ─── Left content column ─────────────────────────────────────────── */ -.content { + padding: 60px 80px 40px; display: flex; flex-direction: column; - gap: 50px; + gap: 24px; } -/* Body + CTAs wrapper (animated together by GSAP) */ -.bodyWrap { - display: flex; - flex-direction: column; - gap: 50px; -} - -/* ─── Heading — type.hero.900 ─────────────────────────────────────── */ +/* ─── Heading — Quantico Bold ─────────────────────────────────────── */ .heading { - font-family: var(--font-base); - font-size: var(--fs-hero); - font-weight: var(--fw-black); + font-family: 'Quantico', sans-serif; + font-size: clamp(36px, 5vw, 64px); + font-weight: 700; line-height: 1.05; + color: var(--color-text); + letter-spacing: -1px; margin: 0; - padding: 0; -} - -.headingWhite { - color: var(--color-white); - display: block; } -/* Second heading line — block container for gradient text + cursor */ -.headingLine2 { +.line { display: block; min-height: 1.05em; } -.headingGradient { - display: inline; - background: linear-gradient(to right, var(--color-primary-alt), var(--color-gradient-end)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -/* Blinking cursor during typewriter */ -.typingCursor { - display: inline; - color: var(--color-primary-alt); - -webkit-text-fill-color: var(--color-primary-alt); - font-weight: var(--fw-light); - animation: cursorBlink 0.6s step-end infinite; - margin-left: 2px; +/* ─── Terminal cursor ─────────────────────────────────────────────── */ +.cursor { + display: inline-block; + color: var(--color-text); + animation: blink 1s step-end infinite; user-select: none; } -/* ─── Body copy — type.body.200 ───────────────────────────────────── */ -.body { - font-family: var(--font-base); - font-size: var(--fs-body); - font-weight: var(--fw-light); - line-height: 1.6; - color: var(--color-white); - margin: 0; -} - -/* ─── CTA row ─────────────────────────────────────────────────────── */ -.ctaRow { - display: flex; - align-items: center; - gap: 12px; - flex-wrap: wrap; +@keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } } -/* ─── CTA buttons — type.btn.700 ─────────────────────────────────── */ -.ctaPrimary, -.ctaSecondary { - display: inline-flex; - align-items: center; - justify-content: center; - height: 54px; - padding: 0 20px; - border-radius: 6px; +/* ─── Subheading ──────────────────────────────────────────────────── */ +.subheading { font-family: var(--font-base); - font-size: var(--fs-btn); - font-weight: var(--fw-bold); - text-decoration: none; - cursor: pointer; - white-space: nowrap; - transition: opacity 0.2s ease, transform 0.15s ease; -} - -.ctaPrimary { - min-width: 165px; - background: var(--color-primary); - color: var(--color-white); - border: none; -} - -.ctaPrimary:hover { - opacity: 0.88; - transform: translateY(-1px); - color: var(--color-white); - text-decoration: none; -} - -.ctaSecondary { - gap: 6px; - background: transparent; - color: var(--color-white); - border: 1px solid var(--color-primary); -} - -.ctaSecondary:hover { - opacity: 0.88; - transform: translateY(-1px); - color: var(--color-white); - text-decoration: none; -} - -/* ─── Hero image ──────────────────────────────────────────────────── */ -.heroImage { - width: 100%; - max-width: 714px; - height: auto; - object-fit: contain; - pointer-events: none; - display: block; - animation: heroFadeIn 0.8s ease-out 0.15s both; -} - -/* ─── Decorative grid dots ────────────────────────────────────────── */ -.gridDotsLg { - position: absolute; - right: 120px; - bottom: 80px; - width: 81px; - height: 81px; - pointer-events: none; - z-index: 1; - opacity: 0.7; -} - -.gridDotsSm { - position: absolute; - right: 490px; - top: 245px; - width: 31px; - height: 31px; - pointer-events: none; - z-index: 1; - opacity: 0.7; + font-size: clamp(20px, 2.5vw, 24px); + font-weight: var(--fw-regular); + color: var(--color-text-muted); + margin: 0; } /* ─── Responsive ──────────────────────────────────────────────────── */ -@media (min-width: 1920px) { - .inner { - max-width: 1660px; - padding: 0 128px; - } - - .heroImage { max-width: 620px; } -} - -@media (max-width: 1680px) { - .heroImage { max-width: 480px; } -} - -@media (max-width: 1440px) { - .inner { padding: 0 96px; } - .heading { font-size: 56px; } - .heroImage { max-width: 420px; } +@media (max-width: 1100px) { + .inner { padding: 48px 48px 32px; } } -@media (max-width: 900px) { - .heroSection { - border-bottom-left-radius: 32px; - border-bottom-right-radius: 32px; - } - .inner :global(.row) { - --bs-gutter-x: 0; - margin-left: 0; - margin-right: 0; - } - .inner { padding: 120px 48px 80px; } - .heading { font-size: 52px; } - .gridDotsLg, .gridDotsSm { display: none; } +@media (max-width: 768px) { + .inner { padding: 40px 28px 28px; } } -@media (max-width: 600px) { - .inner { padding: 100px 24px 60px; } - .heading { font-size: 40px; } - .body { font-size: var(--fs-sm); } - .ctaRow { flex-direction: column; align-items: stretch; } - .ctaPrimary, .ctaSecondary { justify-content: center; width: 100%; } +@media (max-width: 480px) { + .inner { padding: 32px 20px 24px; } } diff --git a/src/components/ProductHighlightsSection/index.js b/src/components/ProductHighlightsSection/index.js new file mode 100644 index 0000000..a55915f --- /dev/null +++ b/src/components/ProductHighlightsSection/index.js @@ -0,0 +1,58 @@ +import React from 'react'; +import styles from './styles.module.css'; + +const highlights = [ + { + icon: ( + + ), + title: 'Open Source', + description: 'MIT License', + }, + { + icon: ( + + ), + title: 'Self-Hosted', + description: 'Full control. No vendor lock-in.', + }, + { + icon: ( + + ), + title: 'Production Ready', + description: 'Battle-tested. For reliability.', + }, +]; + +export default function ProductHighlightsSection() { + return ( +
+
+
+ {highlights.map((h, i) => ( + +
+
{h.icon}
+
+
{h.title}
+
{h.description}
+
+
+ {i < highlights.length - 1 &&
} + + ))} +
+
+
+ ); +} diff --git a/src/components/ProductHighlightsSection/styles.module.css b/src/components/ProductHighlightsSection/styles.module.css new file mode 100644 index 0000000..d0b90f3 --- /dev/null +++ b/src/components/ProductHighlightsSection/styles.module.css @@ -0,0 +1,86 @@ +/* ─── Section ─────────────────────────────────────────────────────── */ +.section { + background: + linear-gradient(to right, transparent 10%, var(--color-border) 10%, var(--color-border) 90%, transparent 90%) no-repeat 0 0 / 100% 1px, + linear-gradient(to right, transparent 10%, var(--color-border) 10%, var(--color-border) 90%, transparent 90%) no-repeat 0 100% / 100% 1px, + var(--color-bg); + padding: var(--section-pt) 0 var(--section-pb); +} + +.inner { + max-width: 1200px; + margin: 0 auto; + padding: 0 80px; +} + +/* ─── Row ─────────────────────────────────────────────────────────── */ +.row { + display: flex; + align-items: center; + justify-content: center; + gap: 0; +} + +/* ─── Item ────────────────────────────────────────────────────────── */ +.item { + display: flex; + align-items: center; + gap: 20px; + flex: 1; + justify-content: center; + padding: 0 40px; +} + +/* ─── Icon ────────────────────────────────────────────────────────── */ +.icon { + color: var(--color-primary); + flex-shrink: 0; +} + +/* ─── Text ────────────────────────────────────────────────────────── */ +.text { + display: flex; + flex-direction: column; + gap: 4px; +} + +.title { + font-family: var(--font-base); + font-size: var(--fs-body-lg); + font-weight: var(--fw-bold); + line-height: 1.1; + color: var(--color-text); +} + +.description { + font-family: var(--font-base); + font-size: var(--fs-sm); + font-weight: var(--fw-regular); + color: var(--color-text-muted); +} + +/* ─── Divider ─────────────────────────────────────────────────────── */ +.divider { + width: 1px; + height: 60px; + background: var(--color-border); + flex-shrink: 0; +} + +/* ─── Responsive ──────────────────────────────────────────────────── */ +@media (max-width: 900px) { + .inner { padding: 0 48px; } + .row { flex-direction: column; gap: 28px; } + .divider { width: 48px; height: 1px; } + .item { padding: 0; justify-content: flex-start; } +} + +@media (max-width: 600px) { + .inner { padding: 0 20px; } + .item { gap: 16px; } + .icon svg { width: 32px; height: 32px; } +} + +@media (max-width: 480px) { + .inner { padding: 0 16px; } +} diff --git a/src/components/SideNavbar/index.js b/src/components/SideNavbar/index.js new file mode 100644 index 0000000..0ef1634 --- /dev/null +++ b/src/components/SideNavbar/index.js @@ -0,0 +1,94 @@ +import React, { useState, useEffect } from 'react'; +import Link from '@docusaurus/Link'; +import styles from './styles.module.css'; + +function DocsIcon() { + return ( + + ); +} + +function GitHubIcon() { + return ( + + ); +} + +export default function SideNavbar() { + const [visible, setVisible] = useState(false); + + useEffect(() => { + // Show side nav after top navbar (72px) has fully left the viewport + const THRESHOLD = 80; + let ticking = false; + + const onScroll = () => { + if (!ticking) { + requestAnimationFrame(() => { + setVisible(window.scrollY > THRESHOLD); + ticking = false; + }); + ticking = true; + } + }; + + window.addEventListener('scroll', onScroll, { passive: true }); + return () => window.removeEventListener('scroll', onScroll); + }, []); + + return ( + + ); +} diff --git a/src/components/SideNavbar/styles.module.css b/src/components/SideNavbar/styles.module.css new file mode 100644 index 0000000..de2a202 --- /dev/null +++ b/src/components/SideNavbar/styles.module.css @@ -0,0 +1,114 @@ +/* ─── Sidebar shell ────────────────────────────────────────────────── */ +.sidebar { + position: fixed; + left: 0; + top: 0; + height: 100vh; + width: 64px; + z-index: 190; + background: var(--color-bg); + border-right: 1px solid var(--color-border); + + display: flex; + flex-direction: column; + align-items: center; + padding: 28px 0; + gap: 20px; + + /* Hidden by default — slides in from left */ + transform: translateX(-100%); + opacity: 0; + transition: + transform 0.38s cubic-bezier(0.34, 1.46, 0.64, 1), + opacity 0.22s ease; + +} + +.visible { + transform: translateX(0); + opacity: 1; +} + +/* ─── Logo ─────────────────────────────────────────────────────────── */ +.logoLink { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 8px; + transition: opacity 0.15s ease; + text-decoration: none; +} + +.logoLink:hover { + opacity: 0.72; + text-decoration: none; +} + +.logoImg { + display: block; +} + +/* ─── Divider ──────────────────────────────────────────────────────── */ +.divider { + width: 28px; + height: 1px; + background: var(--color-border); + flex-shrink: 0; +} + +/* ─── Icon links ───────────────────────────────────────────────────── */ +.iconLink { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 8px; + color: hsl(0, 0%, 55%); + text-decoration: none; + transition: color 0.15s ease, background 0.15s ease; +} + +.iconLink:hover { + color: var(--color-text); + background: var(--color-surface); + text-decoration: none; +} + +/* ─── Demo button ──────────────────────────────────────────────────── */ +.demoBtn { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + height: 32px; + border: 1px solid var(--color-border); + border-radius: 4px; + text-decoration: none; + transition: border-color 0.15s ease, color 0.15s ease; +} + +.demoBtn:hover { + border-color: var(--color-primary); + text-decoration: none; +} + +.demoBtn:hover .demoBtnText { + color: var(--color-primary); +} + +.demoBtnText { + font-family: var(--font-base); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.8px; + color: hsl(0, 0%, 48%); + transition: color 0.15s ease; +} + +/* ─── Mobile: never show ───────────────────────────────────────────── */ +@media (max-width: 768px) { + .sidebar { display: none; } +} diff --git a/src/components/SocialProofSection/index.js b/src/components/SocialProofSection/index.js new file mode 100644 index 0000000..606542a --- /dev/null +++ b/src/components/SocialProofSection/index.js @@ -0,0 +1,114 @@ +import React from 'react'; +import styles from './styles.module.css'; + +function CalendarWithClockIcon() { + return ( + + ); +} + +function StopwatchIcon() { + return ( + + ); +} + +export default function SocialProofSection() { + return ( +
+
+
+ + {/* Left — text */} +
+

1 Month of Infrastructure Work

+

10 Minutes in Autobase

+ +
+ + {/* Right — visual */} +
+ +
+ + 1 MONTH +
+ + {/* Arrow */} + + +
+ +
+ + 10 MINUTES +
+ +
+
+
+
+ ); +} diff --git a/src/components/SocialProofSection/styles.module.css b/src/components/SocialProofSection/styles.module.css new file mode 100644 index 0000000..beeeb68 --- /dev/null +++ b/src/components/SocialProofSection/styles.module.css @@ -0,0 +1,116 @@ +/* ─── Section ─────────────────────────────────────────────────────── */ +.section { + background: var(--color-bg); + padding: 0 0 16px; + margin-bottom: 0; +} + +.inner { + max-width: 1200px; + margin: 0 auto; + padding: 0 80px; +} + +/* ─── Banner card ─────────────────────────────────────────────────── */ +.banner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 60px; + padding: 40px 52px; + border: 1px solid var(--color-border); + border-radius: 12px; + background: var(--color-bg); + box-shadow: + 0 1px 2px rgba(0, 0, 0, 0.04), + 0 4px 8px rgba(0, 0, 0, 0.04), + 0 12px 24px rgba(0, 0, 0, 0.04), + 0 24px 48px rgba(0, 0, 0, 0.03); +} + +/* ─── Text block ──────────────────────────────────────────────────── */ +.text { + display: flex; + flex-direction: column; + gap: 6px; +} + +.before { + font-family: var(--font-base); + font-size: clamp(22px, 2.5vw, 30px); + font-weight: var(--fw-bold); + color: var(--color-text); + margin: 0; + line-height: 1.2; +} + +.after { + font-family: var(--font-base); + font-size: clamp(22px, 2.5vw, 30px); + font-weight: var(--fw-black); + color: var(--color-primary); + margin: 0; + line-height: 1.2; +} + +.underbar { + display: block; + width: 40px; + height: 3px; + background: var(--color-primary); + border-radius: 2px; + margin-top: 10px; +} + +/* ─── Visual block ────────────────────────────────────────────────── */ +.visual { + display: flex; + align-items: center; + gap: 28px; + flex-shrink: 0; +} + +.timeBlock { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; +} + +.timeLabel { + font-family: var(--font-base); + font-size: 12px; + font-weight: var(--fw-bold); + color: var(--color-text); + letter-spacing: 1.2px; +} + +.accentLabel { + color: var(--color-primary); +} + +.arrow { + flex-shrink: 0; +} + +.divider { + width: 1px; + height: 80px; + background: var(--color-border); + flex-shrink: 0; +} + +/* ─── Responsive ──────────────────────────────────────────────────── */ +@media (max-width: 900px) { + .inner { padding: 0 48px; } + .banner { flex-direction: column; gap: 32px; text-align: center; padding: 36px 40px; } + .underbar { margin: 10px auto 0; } +} + +@media (max-width: 600px) { + .inner { padding: 0 20px; } + .banner { flex-direction: column; gap: 24px; padding: 28px 24px; } + .visual { gap: 16px; } + .divider { display: none; } + .before, .after { font-size: clamp(18px, 5vw, 24px); } +} diff --git a/src/components/ValuePropsSection/index.js b/src/components/ValuePropsSection/index.js new file mode 100644 index 0000000..d1a2549 --- /dev/null +++ b/src/components/ValuePropsSection/index.js @@ -0,0 +1,85 @@ +import React, { useRef, useEffect } from 'react'; +import styles from './styles.module.css'; + +const props = [ + { + icon: ( + + ), + title: 'Your Infrastructure', + description: 'Run anywhere. You are in control.', + }, + { + icon: ( + + ), + title: 'Your Data', + description: 'Your data stays where you deploy.', + }, + { + icon: ( + + ), + title: 'Your Security', + description: 'Your network. Your rules.', + }, +]; + +export default function ValuePropsSection() { + const gridRef = useRef(null); + + useEffect(() => { + let ctx; + import('gsap').then(({ gsap }) => + import('gsap/ScrollTrigger').then(({ ScrollTrigger }) => { + gsap.registerPlugin(ScrollTrigger); + ctx = gsap.context(() => { + gsap.fromTo( + gridRef.current?.querySelectorAll(`.${styles.card}`), + { opacity: 0, scale: 0.6, y: -10 }, + { + opacity: 1, + scale: 1, + y: 0, + duration: 0.55, + ease: 'back.out(1.4)', + stagger: 0.12, + scrollTrigger: { + trigger: gridRef.current, + start: 'top 85%', + once: true, + }, + } + ); + }); + }) + ); + return () => ctx?.revert(); + }, []); + + return ( +
+
+
+ {props.map((p) => ( +
+
{p.icon}
+

{p.title}

+

{p.description}

+
+ ))} +
+
+
+ ); +} diff --git a/src/components/ValuePropsSection/styles.module.css b/src/components/ValuePropsSection/styles.module.css new file mode 100644 index 0000000..e91e91e --- /dev/null +++ b/src/components/ValuePropsSection/styles.module.css @@ -0,0 +1,86 @@ +/* ─── Section ─────────────────────────────────────────────────────── */ +.section { + background: var(--color-bg); + padding: var(--section-pt) 0 var(--section-pb); +} + +.inner { + max-width: 1200px; + margin: 0 auto; + padding: 0 80px; +} + +/* ─── Grid ────────────────────────────────────────────────────────── */ +.grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; +} + +/* ─── Card ────────────────────────────────────────────────────────── */ +.card { + display: flex; + flex-direction: column; + gap: 16px; + padding: 32px; + border: 1px solid var(--color-border); + border-radius: 8px; + background: var(--color-bg); + box-shadow: + 0 1px 2px rgba(0, 0, 0, 0.04), + 0 4px 8px rgba(0, 0, 0, 0.03), + 0 12px 24px rgba(0, 0, 0, 0.03), + 0 24px 48px rgba(0, 0, 0, 0.02); + transition: box-shadow 0.2s ease; + opacity: 0; + will-change: transform, opacity; +} + +.card:hover { + box-shadow: + 0 1px 3px rgba(0, 0, 0, 0.06), + 0 6px 16px rgba(0, 0, 0, 0.06), + 0 18px 36px rgba(0, 0, 0, 0.05), + 0 32px 64px rgba(0, 0, 0, 0.04); +} + +/* ─── Icon ────────────────────────────────────────────────────────── */ +.icon { + color: var(--color-text); +} + +/* ─── Title ───────────────────────────────────────────────────────── */ +.title { + font-family: var(--font-base); + font-size: var(--fs-body-lg); + font-weight: var(--fw-bold); + line-height: 1.1; + color: var(--color-text); + margin: 0; +} + +/* ─── Description ─────────────────────────────────────────────────── */ +.description { + font-family: var(--font-base); + font-size: var(--fs-sm); + font-weight: var(--fw-regular); + color: var(--color-text-muted); + line-height: 1.6; + margin: 0; +} + +/* ─── Responsive ──────────────────────────────────────────────────── */ +@media (max-width: 900px) { + .inner { padding: 0 48px; } + .grid { grid-template-columns: 1fr 1fr; gap: 16px; } +} + +@media (max-width: 600px) { + .inner { padding: 0 20px; } + .grid { grid-template-columns: 1fr; gap: 12px; } + .card { padding: 24px 20px; gap: 12px; } +} + +@media (max-width: 480px) { + .inner { padding: 0 16px; } +} diff --git a/src/css/custom.css b/src/css/custom.css index b269b16..8691f95 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -1,4 +1,6 @@ @import url('https://fonts.googleapis.com/css2?family=Zalando+Sans+SemiExpanded:ital,wght@0,200..900;1,200..900&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Quantico:ital,wght@0,400;0,700;1,400;1,700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Elms+Sans:ital,wght@0,100..900;1,100..900&display=swap'); @import 'bootstrap/dist/css/bootstrap-grid.min.css'; /** @@ -8,6 +10,7 @@ */ :root { + /* ── Infima theme ───────────────────────────────────────────────── */ --ifm-color-primary: #0366d6; --ifm-color-primary-dark: #024aaa; --ifm-color-primary-darker: #023e8a; @@ -15,54 +18,69 @@ --ifm-color-primary-light: #2b8cd8; --ifm-color-primary-lighter: #69b0e3; --ifm-color-primary-lightest: #a8d1ef; + --ifm-background-color: #ffffff; --ifm-code-font-size: 95%; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); - - --font-base: 'Zalando Sans SemiExpanded', sans-serif; - --fw-light: 200; - --fw-regular: 400; - --fw-medium: 500; - --fw-semibold: 600; - --fw-bold: 700; - --fw-black: 900; - - --fs-hero: 72px; - --fs-h2: 48px; - --fs-h3: 32px; - --fs-navbar: 27.12px; - --fs-body-lg: 24px; - --fs-body: 20px; - --fs-btn: 18px; - --fs-sm: 16px; - - --color-primary: #ff5722; - --color-primary-alt: #fc6902; - --color-gradient-end: #be987d; - --color-white: #ffffff; - --color-black: #000000; - --color-muted: #c7c7c7; - - --bg-gradient-extra1: linear-gradient(194.94deg, #000000 19.64%, #743001 135.33%); - --section-pt: 120px; - --section-pb: 120px; + --ifm-font-family-base: 'Quantico', sans-serif; + + /* ── Font family ────────────────────────────────────────────────── */ + --font-base: 'Quantico', sans-serif; + + /* ── Font weights ───────────────────────────────────────────────── */ + /* layer: type.*.200 */ --fw-light: 200; + /* layer: type.*.400 */ --fw-regular: 400; + /* layer: type.*.500 */ --fw-medium: 500; + /* layer: type.*.600 */ --fw-semibold: 600; + /* layer: type.*.700 */ --fw-bold: 700; + /* layer: type.*.900 */ --fw-black: 900; + + /* ── Font sizes — ×1.2 scale, clamp() for fluid responsiveness ──── */ + /* 72 → 86 */ --fs-hero: clamp(48px, 6vw, 86px); + /* 48 → 58 */ --fs-h2: clamp(32px, 4vw, 58px); + /* 32 → 38 */ --fs-h3: clamp(22px, 2.8vw, 38px); + /* 27 → 33 */ --fs-navbar: clamp(18px, 2.2vw, 33px); + /* 24 → 29 */ --fs-body-lg: clamp(18px, 2vw, 29px); + /* 20 → 24 */ --fs-body: clamp(16px, 1.7vw, 24px); + /* 18 → 22 */ --fs-btn: clamp(14px, 1.4vw, 22px); + /* 16 → 19 */ --fs-sm: clamp(13px, 1.2vw, 19px); + + /* ── Brand colours ──────────────────────────────────────────────── */ + /* Figma var: COLOR-SECONDARY */ --color-primary: #ff5722; + --color-primary-alt: #fc6902; + --color-gradient-end: #be987d; + --color-white: #ffffff; + --color-black: #000000; + --color-muted: #c7c7c7; + + /* ── Light theme tokens ──────────────────────────────────────────── */ + --color-bg: #ffffff; + --color-bg-alt: #f5f5f5; + --color-surface: #f9f9f9; + --color-text: #111111; + --color-text-muted: #555555; + --color-border: #e0e0e0; + + /* ── Section vertical rhythm ─────────────────────────────────────── */ + --section-pt: 72px; + --section-pb: 72px; } @media (max-width: 900px) { :root { - --section-pt: 80px; - --section-pb: 80px; + --section-pt: 52px; + --section-pb: 52px; } } @media (max-width: 768px) { :root { - --section-pt: 60px; - --section-pb: 60px; + --section-pt: 40px; + --section-pb: 40px; } } [data-theme='dark'] { - --ifm-background-color: #090C10 !important; + --ifm-background-color: #ffffff !important; --ifm-color-primary: #58a6ff; --ifm-color-primary-dark: #3f8cfd; --ifm-color-primary-darker: #2a74d8; @@ -73,138 +91,106 @@ --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); } -html.docs-wrapper[data-theme='light'] { - --ifm-background-color: #F9FAFB; - --ifm-background-surface-color: #F9FAFB; - background-color: #F9FAFB; +/* ─── Global typography ───────────────────────────────────────────── */ +body { + font-family: 'Quantico', sans-serif; + color: var(--color-text); + background-color: var(--color-bg) !important; } -html.docs-wrapper[data-theme='light'] body, -html.docs-wrapper[data-theme='light'] #__docusaurus, -html.docs-wrapper[data-theme='light'] .main-wrapper, -html.docs-wrapper[data-theme='light'] [class^='docsWrapper_'], -html.docs-wrapper[data-theme='light'] [class*=' docsWrapper_'], -html.docs-wrapper[data-theme='light'] [class^='docRoot_'], -html.docs-wrapper[data-theme='light'] [class*=' docRoot_'] { - background-color: #F9FAFB; +h1, h2, h3, h4, h5, h6 { + font-family: 'Quantico', sans-serif; + font-weight: 700; } -html.docs-wrapper[data-theme='light'] main, -html.docs-wrapper[data-theme='light'] [class^='docMainContainer_'], -html.docs-wrapper[data-theme='light'] [class*=' docMainContainer_'], -html.docs-wrapper[data-theme='light'] [class^='docItemContainer_'], -html.docs-wrapper[data-theme='light'] [class*=' docItemContainer_'], -html.docs-wrapper[data-theme='light'] .theme-doc-sidebar-container, -html.docs-wrapper[data-theme='light'] .theme-doc-toc-desktop { - background-color: #F9FAFB; +p { + font-family: var(--font-base); + font-weight: var(--fw-light); } -.landingPage { - --ifm-background-color: #ffffff !important; - --ifm-background-surface-color: #ffffff !important; - --ifm-font-color-base: #000000; - --ifm-heading-color: #000000; - --ifm-color-primary: #0366d6; - --ifm-color-primary-dark: #024aaa; - --ifm-color-primary-darker: #023e8a; - --ifm-color-primary-darkest: #002e6e; - --ifm-color-primary-light: #2b8cd8; - --ifm-color-primary-lighter: #69b0e3; - --ifm-color-primary-lightest: #a8d1ef; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); - background-color: #ffffff; - color: #000000; +/* ─── Navbar — Light theme ────────────────────────────────────────── */ +.navbar { + background: var(--color-bg) !important; + border-bottom: 1px solid var(--color-border) !important; + box-shadow: none !important; } -.landingPage main { - background-color: #ffffff; +/* ─── Navbar hide-on-scroll transition ───────────────────────────── */ +.navbar--fixed-top { + transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 0.3s ease !important; } -@media (max-width: 768px) { - html:has(.landingPage), - body:has(.landingPage) { - overflow-x: hidden; - } +.navbar--scrolled { + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06) !important; +} - .landingPage { - max-width: 100vw; - overflow-x: hidden; - } +/* ─── Mobile navbar sidebar — glass effect ───────────────────────── */ +.navbar-sidebar { + background: rgba(255, 255, 255, 0.97) !important; + backdrop-filter: blur(24px) saturate(160%); + -webkit-backdrop-filter: blur(24px) saturate(160%); + box-shadow: 4px 0 24px rgba(0, 0, 0, 0.08); +} - .landingPage main { - overflow-x: hidden; - } +.navbar-sidebar__brand { + background: rgba(255, 255, 255, 0.95); + border-bottom: 1px solid rgba(0, 0, 0, 0.06); } -/* Logo text styles */ -.navbar__title { - font-weight: 400; - font-size: 1.4rem; - line-height: 1.2; - white-space: nowrap; - font-family: 'Helvetica Neue', Arial, sans-serif; +.navbar-sidebar__items .menu__link { + font-family: var(--font-base); + font-size: var(--fs-btn); + font-weight: var(--fw-medium); + color: var(--color-black); + border-radius: 8px; + transition: background 0.2s ease, color 0.2s ease; } -.navbar { - padding-left: 4rem; - padding-right: 4rem; +.navbar-sidebar__items .menu__link:hover { + background: rgba(255, 87, 34, 0.08); + color: var(--color-primary); } -@media (max-width: 768px) { - .navbar { - padding-left: 1rem; - padding-right: 1rem; - } +.navbar-sidebar__backdrop { + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); } -/* Landing page transparent navbar */ -.navbar.navbar--transparent { - background: rgba(255, 255, 255, 0.06); - backdrop-filter: blur(48px) saturate(200%) brightness(1.08); - -webkit-backdrop-filter: blur(48px) saturate(200%) brightness(1.08); - border-bottom: 1px solid rgba(255, 255, 255, 0.14); - box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.22), /* specular top highlight */ - inset 0 -1px 0 rgba(255, 255, 255, 0.06), /* subtle bottom rim */ - 0 8px 32px rgba(0, 0, 0, 0.06); - transition: - background 0.4s ease, - box-shadow 0.4s ease, - border-color 0.4s ease, - backdrop-filter 0.4s ease; -} - -.navbar.navbar--transparent::before { - content: ''; - position: absolute; - inset: 0; - background: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.10) 0%, - rgba(255, 255, 255, 0.03) 55%, - rgba(255, 255, 255, 0.00) 100% - ); - pointer-events: none; - z-index: 0; -} - -.navbar.navbar--transparent.navbar--scrolled { - background: rgba(255, 255, 255, 0.12); - border-bottom-color: rgba(255, 255, 255, 0.20); - box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.28), - inset 0 -1px 0 rgba(255, 255, 255, 0.08), - 0 8px 40px rgba(0, 0, 0, 0.10); +/* ─── Navbar brand/logo sizing ───────────────────────────────────── */ +.navbar__brand { + display: flex; + align-items: center; + gap: 8px; } -@media (max-width: 768px) { - .navbar.navbar--transparent, - .navbar.navbar--transparent.navbar--scrolled { - backdrop-filter: blur(32px) saturate(180%) brightness(1.06); - -webkit-backdrop-filter: blur(32px) saturate(180%) brightness(1.06); - } +.navbar__logo { + height: 28px; + width: auto; } -.navbar.navbar--transparent :is(.navbar__item, .navbar__link, .navbar__brand) { - transition: color 0.2s ease; +.navbar__title { + font-family: var(--font-base); + font-weight: var(--fw-bold); + font-size: 18px; +} + +@media (max-width: 768px) { + .navbar__logo { height: 24px; } + .navbar__title { font-size: 16px; } +} + +/* ─── Docs pages — light theme (orange accent) ────────────────────── */ +html.docs-doc-page { + --ifm-color-primary: var(--color-primary); + --ifm-color-primary-dark: var(--color-primary-alt); + --ifm-color-primary-darker: #e04d1a; + --ifm-color-primary-darkest: #c23c0e; + --ifm-color-primary-light: #ff7043; + --ifm-color-primary-lighter: #ff8a65; + --ifm-color-primary-lightest: #ffab91; + --ifm-link-color: var(--color-primary); + --ifm-link-hover-color: var(--color-primary-alt); + --ifm-tabs-color-active: var(--color-primary); } diff --git a/src/pages/index.js b/src/pages/index.js index 95a3244..a91c394 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,46 +1,38 @@ import React, { useEffect } from 'react'; import Layout from '@theme/Layout'; import HeroSection from '@site/src/components/HeroSection'; -import AboutSection from '@site/src/components/AboutSection'; -import ProblemSection from '@site/src/components/ProblemSection'; -import WhatIsAutobaseSection from '@site/src/components/WhatIsAutobaseSection'; -import ExplainSection from '@site/src/components/ExplainSection'; -import HowItWorksSection from '@site/src/components/HowItWorksSection'; -import FeaturedSection from '@site/src/components/FeaturedSection'; -import CloudProviders from '@site/src/components/CloudProviders'; -import VideoSection from '@site/src/components/VideoSection'; -import WhatYouGetSection from '@site/src/components/WhatYouGetSection'; -import PricingSection from '@site/src/components/PricingSection'; -import ComparisonSection from '@site/src/components/ComparisonSection'; -import Sponsors from '@site/src/components/Sponsors'; +import ArchDiagramSection from '@site/src/components/ArchDiagramSection'; +import ValuePropsSection from '@site/src/components/ValuePropsSection'; +import ProductHighlightsSection from '@site/src/components/ProductHighlightsSection'; +import CLISection from '@site/src/components/CLISection'; +import ContactSection from '@site/src/components/ContactSection'; +import SocialProofSection from '@site/src/components/SocialProofSection'; -function HomepageContent() { +export default function Home() { // GSAP scroll reveal — blur + fade from below, fires once per section useEffect(() => { - if (typeof window !== 'undefined' && window.matchMedia('(max-width: 768px)').matches) { - return undefined; - } - let ctx; Promise.all([import('gsap'), import('gsap/ScrollTrigger')]).then( ([{ gsap }, { ScrollTrigger }]) => { gsap.registerPlugin(ScrollTrigger); ctx = gsap.context(() => { document.querySelectorAll('main section').forEach((section) => { - gsap.from(section, { - y: 48, - opacity: 0, - duration: 1.0, - ease: 'expo.out', - immediateRender: true, - scrollTrigger: { - trigger: section, - start: 'top 88%', - once: true, - invalidateOnRefresh: true, - toggleActions: 'play none none none', - }, - }); + gsap.fromTo( + section, + { y: 32, opacity: 0, filter: 'blur(6px)' }, + { + y: 0, + opacity: 1, + filter: 'blur(0px)', + duration: 0.85, + ease: 'expo.out', + scrollTrigger: { + trigger: section, + start: 'top 90%', + once: true, + }, + } + ); }); }); } @@ -48,34 +40,19 @@ function HomepageContent() { return () => ctx?.revert(); }, []); - return ( - <> - -
- {/* */} - {/* */} - - - - - - - - - {/* */} - -
- - ); -} - -export default function Home() { return ( - + +
+ + + + + + +
); } diff --git a/src/pages/index.module.css b/src/pages/index.module.css index e635b07..71c44aa 100644 --- a/src/pages/index.module.css +++ b/src/pages/index.module.css @@ -67,7 +67,7 @@ } /* Adaptive style for mobile devices */ -@media (max-width: 1440px) { +@media (max-width: 1200px) { .heroBanner_description, .heroLead, .heroBenefits { diff --git a/src/theme/Footer/index.js b/src/theme/Footer/index.js index 489b131..e201d64 100644 --- a/src/theme/Footer/index.js +++ b/src/theme/Footer/index.js @@ -1,163 +1,33 @@ import React from 'react'; -import useBaseUrl from '@docusaurus/useBaseUrl'; -import { useLocation } from '@docusaurus/router'; -import Link from '@docusaurus/Link'; -import OriginalFooter from '@theme-original/Footer'; -import clsx from 'clsx'; import styles from './styles.module.css'; -const navGroups = [ - { - title: 'Docs', - links: [{ label: 'Introduction', href: '/docs/' }], - }, - { - title: 'Support', - links: [{ label: 'Support Packages', href: '/docs/support' }], - }, - { - title: 'Source code', - links: [{ label: 'GitHub', href: 'https://github.com/autobase-tech/autobase', external: true }], - }, -]; - -function normalizePath(pathname) { - if (!pathname || pathname === '/') { - return '/'; - } - - return pathname.endsWith('/') ? pathname.slice(0, -1) : pathname; -} - -export default function Footer(props) { - const { pathname } = useLocation(); - const homePath = useBaseUrl('/'); - const isHomepage = normalizePath(pathname) === normalizePath(homePath); - - if (!isHomepage) { - return ; - } - +export default function Footer() { return ( -
-
- - {/* Decorative vector — absolutely positioned, clips on right edge */} - +
+
-
+
- {/* ── Hero area: hero text + CTAs ── */} -
-
- - {/* type.hero.900 */} -

- Run PostgreSQL
- as a system. -

- - {/* type.body-lg.500 */} -

Not as a set of servers.

- - {/* CTAs */} -
- - Start a Free trial now - - - Chat on Telegram - - -
- -
-
- - {/* ── Footer bottom: nav + social + copyright ── */} -
+
+ + Autobase +
- {/* Nav groups + social */} -
- {navGroups.map((group) => ( -
-
- {group.title} - {group.links.map((link) => - link.external ? ( - - {link.label} - - ) : ( - - {link.label} - - ) - )} -
-
- ))} +

+ Copyright © 2019 - 2026. All rights reserved. +

- {/* Social links */} - -
+

+ Payments are processed by NovaBridge Tech OÜ,
+ which acts as the authorized payment and billing partner for Autobase. +

- {/* Copyright */} -
-
- - autobase -
-

- Copyright © 2019 - 2026. All rights reserved.
-

-

- Payments are processed by NovaBridge Tech OÜ, which acts as the authorized payment and billing partner for Autobase.
- NovaBridge Tech OÜ. Reg. nr 17390133 · Võru tn 11, Lasnamäe linnaosa, Tallinn 13612, Harjumaa, Estonia -

-
+

+ NovaBridge Tech OÜ. Reg. nr 17390133 · Võru tn 11,
+ Lasnamäe linnaosa, Tallinn 13612, Harjumaa, Estonia +

-
-
-
-
+
+ ); } diff --git a/src/theme/Footer/styles.module.css b/src/theme/Footer/styles.module.css index 31eb8c6..27a4f65 100644 --- a/src/theme/Footer/styles.module.css +++ b/src/theme/Footer/styles.module.css @@ -1,251 +1,70 @@ -/* ─── Footer wrapper ─────────────────────────────────────────────── */ -.footerSurface { - background-color: #ffffff; -} - +/* ─── Footer ──────────────────────────────────────────────────────── */ .footer { - display: flex; - flex-direction: column; - position: relative; - overflow: hidden; - border-top-left-radius: 48px; - border-top-right-radius: 48px; - background-color: #f7f5f3; - min-height: 100vh; + background: var(--color-bg); } -/* ─── Inner — constrained width, full height ──────────────────────── */ -.inner { - flex: 1; - display: flex; - flex-direction: column; - max-width: 1540px; - width: 100%; +.divider { + width: 80%; + height: 1px; + background: var(--color-border); margin: 0 auto; - padding: 0 160px; - position: relative; - z-index: 1; -} - -/* ─── Decorative vector — right-anchored, bleeds off edge ─────────── */ -.vectorLine { - position: absolute; - right: -100px; - top: 36px; - width: 820px; - height: 717px; - pointer-events: none; - z-index: 0; -} - -/* ─── Hero area — fills remaining height, centers content ────────── */ -.heroArea { - flex: 1; - display: flex; - align-items: center; - padding: 80px 0; -} - -.heroContent { - display: flex; - flex-direction: column; - gap: 25px; - max-width: 535px; -} - -/* type.hero.900 */ -.heroHeading { - font-family: var(--font-base); - font-size: var(--fs-hero); - font-weight: var(--fw-black); - color: var(--color-black); - line-height: 1.05; - margin: 0; -} - -/* type.body-lg.500 */ -.heroSubtext { - font-family: var(--font-base); - font-size: var(--fs-body-lg); - font-weight: var(--fw-medium); - color: var(--color-black); - margin: 0; -} - -.orange { color: var(--color-primary); } - -/* ─── CTAs ───────────────────────────────────────────────────────── */ -.ctaRow { - display: flex; - align-items: center; - gap: 12px; - flex-wrap: wrap; -} - -.ctaPrimary { - display: inline-flex; - align-items: center; - justify-content: center; - height: 54px; - padding: 0 28px; - background: var(--color-primary); - color: var(--color-white); - border-radius: 6px; - font-family: var(--font-base); - font-size: var(--fs-btn); - font-weight: var(--fw-bold); - text-decoration: none; - transition: opacity 0.15s ease; -} - -.ctaPrimary:hover { - color: var(--color-white); - opacity: 0.9; - text-decoration: none; -} - -.ctaSecondary { - display: inline-flex; - align-items: center; - gap: 6px; - height: 54px; - padding: 0 20px; - border: 1.5px solid var(--color-primary); - color: var(--color-black); - border-radius: 6px; - font-family: var(--font-base); - font-size: var(--fs-btn); - font-weight: var(--fw-bold); - text-decoration: none; - transition: opacity 0.15s ease; -} - -.ctaSecondary:hover { - color: var(--color-black); - opacity: 0.85; - text-decoration: none; -} - -/* ─── Footer bottom ──────────────────────────────────────────────── */ -.footerBottom { - padding: 60px 0 44px; - border-top: 1px solid rgba(0, 0, 0, 0.08); } -.navRow { - margin-bottom: 40px; -} - -/* ─── Nav groups ─────────────────────────────────────────────────── */ -.navGroup { - display: flex; - flex-direction: column; - gap: 10px; - margin-right: 80px; - margin-bottom: 24px; -} - -/* type.body-lg.600 */ -.navTitle { - font-family: var(--font-base); - font-size: var(--fs-body-lg); - font-weight: var(--fw-semibold); - color: var(--color-black); -} - -/* type.caption.400 */ -.navLink { - font-family: var(--font-base); - font-size: var(--fs-sm); - font-weight: var(--fw-regular); - color: rgba(0, 0, 0, 0.6); - text-decoration: none; - transition: color 0.15s ease; -} - -.navLink:hover { color: var(--color-black); text-decoration: none; } - -/* ─── Social links ───────────────────────────────────────────────── */ -.socialGroup { - display: flex; - flex-direction: column; - gap: 10px; - margin-bottom: 24px; -} - -.socialItem { - display: flex; - align-items: center; - gap: 10px; - font-family: var(--font-base); - font-size: var(--fs-sm); - font-weight: var(--fw-regular); - color: rgba(0, 0, 0, 0.6); - text-decoration: none; - transition: color 0.15s ease; -} - -.socialItem:hover { color: var(--color-black); text-decoration: none; } - -/* ─── Copyright row ──────────────────────────────────────────────── */ -.copyrightRow { +.inner { + max-width: 1200px; + margin: 0 auto; + padding: 48px 80px 56px; display: flex; flex-direction: column; - gap: 10px; + gap: 20px; } +/* ─── Logo ────────────────────────────────────────────────────────── */ .logoMark { display: flex; align-items: center; gap: 10px; + margin-bottom: 4px; } -/* type.navbar.400 */ .logoText { font-family: var(--font-base); - font-size: var(--fs-navbar); + font-size: 20px; font-weight: var(--fw-regular); - color: var(--color-black); + color: var(--color-text); + line-height: 1; } -/* type.caption.400 */ +/* ─── Copyright ───────────────────────────────────────────────────── */ .copyright { font-family: var(--font-base); font-size: var(--fs-sm); font-weight: var(--fw-regular); - color: rgba(0, 0, 0, 0.6); + color: var(--color-text); margin: 0; + line-height: 1.6; } -.billingInfo { +/* ─── Legal text ──────────────────────────────────────────────────── */ +.legal { font-family: var(--font-base); - font-size: 14px; + font-size: var(--fs-sm); font-weight: var(--fw-regular); - color: rgba(0, 0, 0, 0.6); - line-height: 1.45; + color: var(--color-text-muted); margin: 0; + line-height: 1.7; } -/* ─── Responsive ─────────────────────────────────────────────────── */ -@media (min-width: 1920px) { - .inner { - max-width: 1700px; - padding: 0 112px; - } -} - -@media (max-width: 1440px) { - .inner { padding: 0 96px; } +/* ─── Responsive ──────────────────────────────────────────────────── */ +@media (max-width: 900px) { + .inner { padding: 40px 48px 48px; } } -@media (max-width: 900px) { - .inner { padding: 0 48px; } - .heroHeading { font-size: var(--fs-h2); } +@media (max-width: 600px) { + .inner { padding: 32px 20px 40px; } + .divider { width: 90%; } } -@media (max-width: 768px) { - .inner { padding: 0 24px; } - .heroHeading { font-size: var(--fs-h3); } - .vectorLine { display: none; } - .navGroup { margin-right: 0; } +@media (max-width: 480px) { + .inner { padding: 28px 16px 36px; } } diff --git a/src/theme/Navbar/index.js b/src/theme/Navbar/index.js index 1145411..275c36b 100644 --- a/src/theme/Navbar/index.js +++ b/src/theme/Navbar/index.js @@ -1,50 +1,17 @@ import React from 'react'; -import useBaseUrl from '@docusaurus/useBaseUrl'; -import { useLocation } from '@docusaurus/router'; -import { useThemeConfig } from '@docusaurus/theme-common'; -import { useHideableNavbar } from '@docusaurus/theme-common/internal'; import Link from '@docusaurus/Link'; -import OriginalNavbar from '@theme-original/Navbar'; import clsx from 'clsx'; import styles from './styles.module.css'; -function normalizePath(pathname) { - if (!pathname || pathname === '/') { - return '/'; - } - - return pathname.endsWith('/') ? pathname.slice(0, -1) : pathname; -} - -export default function Navbar(props) { - const { pathname } = useLocation(); - const { - navbar: { hideOnScroll }, - } = useThemeConfig(); - const homePath = useBaseUrl('/'); - const isHomepage = normalizePath(pathname) === normalizePath(homePath); - const { navbarRef, isNavbarVisible } = useHideableNavbar( - isHomepage && hideOnScroll - ); - - if (!isHomepage) { - return ( -
- -
- ); - } +const navLinks = [ + { label: '/docs', to: '/docs' }, + { label: '/github', href: 'https://github.com/autobase-tech/autobase' }, + { label: '/demo', href: 'https://demo.autobase.tech' }, +]; +export default function Navbar() { return ( -