diff --git a/website/app/(home)/styles/home.css b/website/app/(home)/styles/home.css index 473c0bc..a061509 100644 --- a/website/app/(home)/styles/home.css +++ b/website/app/(home)/styles/home.css @@ -57,6 +57,49 @@ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35), inset 0 -56px 60px -34px var(--accent-glow); } +/* Lens sheen — a soft specular glint drifts across the glass, like light + catching a lens. No hard scan-line. Slow, with a long calm pause between + passes. Reduced-motion: nothing moves. */ +.hero-mascot-stage::before { + content: ''; + position: absolute; + inset: -30%; + z-index: 2; + pointer-events: none; + opacity: 0; + background: linear-gradient( + 100deg, + transparent 41%, + rgba(226, 246, 255, 0.1) 47%, + rgba(240, 251, 255, 0.34) 50%, + rgba(226, 246, 255, 0.1) 53%, + transparent 59% + ); + mix-blend-mode: screen; + transform: translateX(-58%); +} +@media (prefers-reduced-motion: no-preference) { + .hero-mascot-stage::before { + animation: lensSheen 7s var(--ease-out) infinite; + } +} +@keyframes lensSheen { + 0% { + transform: translateX(-58%); + opacity: 0; + } + 6% { + opacity: 1; + } + 19% { + opacity: 1; + } + 27%, + 100% { + transform: translateX(58%); + opacity: 0; + } +} .hero-say { font-size: 0.9rem; color: var(--muted); @@ -66,14 +109,21 @@ text-align: center; } .hero-title { - font-size: clamp(2.5rem, 1rem + 5vw, 4.6rem); + font-size: clamp(2.6rem, 0.9rem + 5.4vw, 5rem); font-weight: 800; - letter-spacing: -0.042em; - line-height: 0.98; + letter-spacing: -0.044em; + line-height: 0.94; color: var(--text-strong); margin: 18px 0 0; text-align: left; } + +/* The verdict keyword — vivid blue, the one word the headline turns on. */ +.hero-mark { + display: inline-block; + color: var(--warm); + white-space: nowrap; +} .hero-sub { max-width: 52ch; margin: 22px 0 0; @@ -158,12 +208,71 @@ } .vd-card { + position: relative; border-radius: 18px; background: linear-gradient(180deg, var(--surface-2), var(--surface)); border: 1px solid var(--border-2); box-shadow: var(--shadow-lg); overflow: hidden; } + +/* ── Holographic verdict card — tilts toward the cursor with an iridescent foil + sweep + a glare that tracks the pointer. JS adds .is-holo on desktop only + (pointer:fine, motion OK). Content stays legible: the foil is a blended + sheen, intensifying on hover, not a wash. ──────────────────────────────── */ +.vd-card-stage { + perspective: 1300px; +} +.vd-card.is-holo { + transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); + transition: transform 0.2s var(--ease-out); + will-change: transform; +} +.vd-foil, +.vd-glare { + position: absolute; + inset: 0; + z-index: 6; + border-radius: inherit; + pointer-events: none; + opacity: 0; + transition: opacity 0.3s var(--ease-out); +} +/* The foil: a broad cyan→blue→silver→coral→lavender band that drifts with the + pointer (matches the reference holographic sheet). Faint at rest, vivid on tilt. */ +.vd-card.is-holo .vd-foil { + opacity: calc(0.14 + var(--active, 0) * 0.54); + mix-blend-mode: hard-light; + background: linear-gradient( + 115deg, + transparent 6%, + rgba(6, 182, 212, 0.55) 20%, + rgba(37, 99, 255, 0.45) 33%, + rgba(255, 255, 255, 0.62) 45%, + rgba(255, 90, 95, 0.46) 58%, + rgba(167, 139, 250, 0.5) 72%, + transparent 90% + ); + background-size: 200% 200%; + background-position: calc(var(--px, 50%) * 0.7 + 15%) calc(var(--py, 50%) * 0.7 + 15%); +} +/* The glare: a soft white spotlight that sits under the cursor. */ +.vd-card.is-holo .vd-glare { + opacity: calc(var(--active, 0) * 0.6); + mix-blend-mode: soft-light; + background: radial-gradient( + circle at var(--px, 50%) var(--py, 50%), + rgba(255, 255, 255, 0.75), + rgba(255, 255, 255, 0) 42% + ); +} +@media (prefers-reduced-motion: reduce) { + .vd-card.is-holo { + transform: none; + transition: none; + } +} + .vd-card-bar { display: flex; align-items: center; @@ -229,28 +338,58 @@ color: var(--muted); margin-top: 5px; } +/* Tabs are a real tablist now — horizontally scrollable, with a fade mask on + the overflow edge so it reads as "more to the right". */ .vd-tabs { display: flex; gap: 16px; padding: 0 20px; border-bottom: 1px solid var(--border); - overflow: hidden; - mask-image: linear-gradient(90deg, #000 86%, transparent); + overflow-x: auto; + scrollbar-width: none; + scroll-snap-type: x proximity; + mask-image: linear-gradient(90deg, #000 92%, transparent); +} +.vd-tabs::-webkit-scrollbar { + display: none; } .vd-tab { - font-size: 0.82rem; - font-weight: 600; + font: 600 0.82rem/1 var(--sans); color: var(--muted); - padding: 11px 0; + padding: 11px 1px 10px; white-space: nowrap; + scroll-snap-align: start; + background: none; + border: none; border-bottom: 2px solid transparent; + cursor: pointer; + transition: color var(--dur-fast) var(--ease-out), + border-color var(--dur-fast) var(--ease-out); +} +.vd-tab:hover { + color: var(--text-strong); } .vd-tab.is-active { color: var(--text-strong); border-bottom-color: var(--warm); } +.vd-tab:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 3px; + border-radius: 4px; +} + +/* The swappable card body. A fixed min-height keeps the card from jumping as + panels of different lengths cross-fade. */ +.vd-panel { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px 20px 20px; + min-height: 178px; +} + .vd-verdict { - margin: 18px 20px 0; display: flex; align-items: center; gap: 16px; @@ -278,7 +417,6 @@ color: var(--sub); } .vd-bottom { - margin: 12px 20px 0; padding: 14px 16px; border-radius: 12px; background: var(--bg-2); @@ -299,13 +437,13 @@ display: flex; align-items: center; gap: 10px; - padding: 14px 20px 20px; + margin-top: auto; } .vd-lang-chip { font: 700 11px/1 var(--mono); - color: var(--warn); - background: color-mix(in srgb, var(--warn) 16%, transparent); - border: 1px solid color-mix(in srgb, var(--warn) 36%, transparent); + color: var(--accent); + background: var(--accent-weak); + border: 1px solid var(--accent-line); border-radius: 6px; padding: 6px 9px; } @@ -314,6 +452,189 @@ color: var(--faint); } +/* ── Verdict panel content (ELI5 / Technical / Use Cases / Health / etc.) ── */ +.vd-prose p { + font-size: 0.92rem; + color: var(--text); + margin: 0 0 10px; +} +.vd-prose p:last-child { + margin-bottom: 0; +} +.vd-prose code, +.vd-list code { + font: 600 0.84em/1 var(--mono); + color: var(--accent); + background: var(--accent-weak); + border-radius: 4px; + padding: 1px 5px; +} +.vd-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 12px; +} +.vd-list li { + position: relative; + padding-left: 22px; + font-size: 0.9rem; + color: var(--sub); +} +.vd-list li::before { + content: ''; + position: absolute; + left: 0; + top: 0.5em; + width: 8px; + height: 8px; + border-radius: 2px; + background: var(--warm); + transform: rotate(45deg); +} +.vd-list strong { + color: var(--text-strong); + font-weight: 700; +} +.vd-cases { + display: flex; + flex-wrap: wrap; + gap: 8px; +} +.vd-case { + font: 600 0.84rem/1 var(--sans); + color: var(--text); + background: var(--surface); + border: 1px solid var(--border-2); + border-radius: 999px; + padding: 9px 14px; +} +.vd-bars { + display: flex; + flex-direction: column; + gap: 12px; +} +.vd-bar { + display: grid; + grid-template-columns: 9.5ch 1fr 2.5ch; + align-items: center; + gap: 12px; +} +.vd-bar-k { + font: 600 0.8rem/1 var(--sans); + color: var(--sub); +} +.vd-bar-track { + height: 7px; + border-radius: 999px; + background: var(--bg-2); + border: 1px solid var(--border); + overflow: hidden; +} +.vd-bar-fill { + display: block; + height: 100%; + border-radius: inherit; + background: linear-gradient(90deg, var(--warm-2), var(--warm)); +} +.vd-bar-v { + font: 700 0.82rem/1 var(--mono); + color: var(--text-strong); + text-align: right; +} +.vd-clean { + display: flex; + align-items: center; + gap: 14px; + padding: 16px; + border-radius: 12px; + background: color-mix(in srgb, var(--ok) 9%, var(--bg-2)); + border: 1px solid color-mix(in srgb, var(--ok) 32%, transparent); +} +.vd-clean-mark { + display: inline-grid; + place-items: center; + width: 36px; + height: 36px; + flex-shrink: 0; + border-radius: 50%; + color: var(--ok); + background: color-mix(in srgb, var(--ok) 16%, transparent); +} +.vd-clean-k { + font: 700 0.95rem/1.2 var(--sans); + color: var(--text-strong); + margin-bottom: 4px; +} +.vd-clean p { + font-size: 0.88rem; + color: var(--sub); +} +.vd-stack { + margin: 0; + display: flex; + flex-direction: column; + gap: 11px; +} +.vd-stack > div { + display: grid; + grid-template-columns: 11ch 1fr; + gap: 14px; + padding-bottom: 11px; + border-bottom: 1px solid var(--border); +} +.vd-stack > div:last-child { + border-bottom: none; + padding-bottom: 0; +} +.vd-stack dt { + font: 700 10px/1.5 var(--mono); + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--muted); +} +.vd-stack dd { + margin: 0; + font-size: 0.9rem; + color: var(--text); +} + +/* Panel crossfade on tab switch + the verdict stamp's heavy slam on scroll-in. + Both gated — reduced-motion visitors get an instant swap and a static stamp. */ +@media (prefers-reduced-motion: no-preference) { + .vd-panel { + animation: vdPanelIn 260ms var(--ease-out); + } + .vd-panel.is-in .vd-stamp { + animation: vdStampSlam 460ms var(--ease-stamp) both; + } +} +@keyframes vdPanelIn { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: none; + } +} +@keyframes vdStampSlam { + 0% { + opacity: 0; + transform: rotate(11deg) scale(2.3); + } + 68% { + opacity: 0.9; + } + 100% { + opacity: 0.9; + transform: rotate(-7deg) scale(1); + } +} + /* ── Feature bento ───────────────────────────────────────────────────── */ .bento { margin-top: 32px; @@ -322,6 +643,8 @@ gap: 14px; } .feat { + position: relative; + overflow: hidden; grid-column: span 1; border-radius: 16px; padding: 22px 20px; @@ -330,11 +653,25 @@ transition: transform var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out); } +/* Accent edge-light — a brass hairline that draws across the top on hover. */ +.feat::after { + content: ''; + position: absolute; + inset: 0 0 auto 0; + height: 2px; + background: var(--grad-soft); + transform: scaleX(0); + transform-origin: left; + transition: transform var(--dur-slow) var(--ease-out); +} .feat:hover { - transform: translateY(-4px); - border-color: var(--border-2); + transform: translateY(-5px); + border-color: var(--accent-line); box-shadow: var(--shadow-md); } +.feat:hover::after { + transform: scaleX(1); +} .feat-wide { grid-column: span 2; background: radial-gradient(420px 200px at 100% 0%, var(--accent-weak), transparent 62%), @@ -349,15 +686,26 @@ color: var(--accent); background: var(--accent-weak); border: 1px solid var(--accent-line); + transition: transform var(--dur) var(--ease-spring), background var(--dur) var(--ease-out), + border-color var(--dur) var(--ease-out); +} +/* The lens tilts and lifts as the card is inspected. */ +.feat:hover .feat-ic { + transform: translateY(-2px) scale(1.07) rotate(-4deg); + background: var(--accent-weak); + border-color: var(--accent); +} +.feat-wide:hover .feat-ic { + border-color: var(--pop); } .feat-ic .icon { display: block; } -/* The wide "hero" feature tiles take the warm accent — the complementary pop. */ +/* The wide "hero" feature tiles take the hot-coral pop — the complementary jolt. */ .feat-wide .feat-ic { - color: var(--warm); - background: var(--warm-weak); - border-color: var(--warm-line); + color: var(--pop); + background: var(--pop-weak); + border-color: var(--pop-line); } /* Line icons are drawn in by GSAP on scroll (pathLength=1); fully drawn by default so they're visible without JS / under reduced-motion. */ @@ -531,6 +879,11 @@ } /* ── Final CTA ───────────────────────────────────────────────────────── */ +/* Pull the closing CTA up — the generic section top padding left it floating in + a void between the showcase strip and the footer. */ +.site-root .final-cta { + padding-top: clamp(36px, 3.5vw, 60px); +} .final-cta-inner { text-align: center; display: flex; diff --git a/website/app/(home)/styles/shell.css b/website/app/(home)/styles/shell.css index 111e06e..5a89a7e 100644 --- a/website/app/(home)/styles/shell.css +++ b/website/app/(home)/styles/shell.css @@ -53,7 +53,7 @@ inset: 0; z-index: 60; pointer-events: none; - opacity: 0.04; + opacity: 0.02; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-size: 180px 180px; } @@ -332,8 +332,8 @@ width: 7px; height: 7px; border-radius: 50%; - background: var(--accent); - box-shadow: 0 0 10px var(--accent); + background: var(--pop); + box-shadow: 0 0 10px var(--pop); } .site-root .section-title { font-size: clamp(1.7rem, 1.1rem + 1.8vw, 2.6rem); @@ -411,7 +411,7 @@ /* ── Footer ──────────────────────────────────────────────────────────── */ .site-footer { flex-shrink: 0; - margin-top: clamp(60px, 6vw, 110px); + margin-top: clamp(36px, 3.5vw, 64px); border-top: 1px solid var(--border); background: color-mix(in srgb, var(--surface) 50%, transparent); } diff --git a/website/app/global.css b/website/app/global.css index 4f55598..4efbff5 100644 --- a/website/app/global.css +++ b/website/app/global.css @@ -5,10 +5,11 @@ @source '../components'; /* ════════════════════════════════════════════════════════════════════════ - "The Case File" — a detective/inspector identity for RepoLens. - Leads LIGHT (warm manila + ink), with an amber/brass lens-glint accent and a - detective red for risk. Dark survives as a warm "night stakeout" (.dark), not - a cold terminal. next-themes flips .dark; tokens drive marketing + docs. + RepoLens — "Inspector" identity, bright & alive. + Leads LIGHT on a clean cool near-white (no warmth — brown reads cheap), with + near-black ink, a VIVID blue lead, a cyan scanner glint, and a hot-coral pop + for life. Green = FIT, red = RISK. Dark is a cool slate complement, not a + warm noir and not an AI terminal. next-themes flips .dark; tokens drive all. ──────────────────────────────────────────────────────────────────────── */ :root { @@ -25,120 +26,158 @@ /* the stamp's heavy "slam" */ --ease-stamp: cubic-bezier(0.5, 1.8, 0.4, 0.95); - /* Amber/brass lens-glint accent + detective red. One ramp tuned for paper; - .dark brightens it for the night-stakeout surfaces. */ - --accent: #9e530e; - --accent-2: #934a0c; - --accent-3: #d9822b; - --grad: linear-gradient(120deg, #d9822b 0%, #9e530e 60%, #934a0c 120%); - --grad-soft: linear-gradient(120deg, #d9822b, #9e530e); - --accent-glow: rgba(179, 94, 16, 0.35); - --accent-weak: rgba(179, 94, 16, 0.1); - --accent-line: rgba(179, 94, 16, 0.28); + /* Vivid blue lead + cyan scanner glint + hot-coral pop. Green=FIT, red=RISK. + .dark recasts these brighter on cool slate. */ + --accent: #2563ff; + --accent-2: #1d4ed8; + --accent-3: #06b6d4; + --glint: #06b6d4; + --grad: linear-gradient(120deg, #06b6d4 0%, #2563ff 58%, #1d4ed8 120%); + --grad-soft: linear-gradient(120deg, #3b82f6, #2563ff); + --accent-glow: rgba(37, 99, 255, 0.3); + --accent-weak: rgba(37, 99, 255, 0.08); + --accent-line: rgba(37, 99, 255, 0.22); - /* warm = the amber CTA; red = the RISKY stamp / red flags */ - --warm: #c2691c; - --warm-2: #d9822b; - --warm-ink: #2a1605; - --warm-glow: rgba(194, 105, 28, 0.4); - --warm-weak: rgba(194, 105, 28, 0.12); - --warm-line: rgba(194, 105, 28, 0.34); - --red: #c0392b; - --red-weak: rgba(192, 57, 43, 0.12); + /* warm = the primary CTA (vivid→deep blue, white text); pop = hot-coral life */ + --warm: #2563ff; + --warm-2: #1d4ed8; + --warm-ink: #ffffff; + --warm-glow: rgba(37, 99, 255, 0.38); + --warm-weak: rgba(37, 99, 255, 0.1); + --warm-line: rgba(37, 99, 255, 0.26); + --pop: #ff5a5f; + --pop-weak: rgba(255, 90, 95, 0.12); + --pop-line: rgba(255, 90, 95, 0.34); + --red: #e5484d; + --red-weak: rgba(229, 72, 77, 0.12); - /* Case File — warm manila paper, sepia ink */ - --bg: #f4ead4; - --bg-2: #ecdfc2; - --surface: #fbf5e7; - --surface-2: #f5ecd6; - --border: #ddccab; - --border-2: #c8b285; - --text: #2a2114; - --text-strong: #17110a; - --sub: #5d5034; - --muted: #756544; - --faint: #a89571; - --ok: #2c7631; - --info: #1d6fa3; - --warn: #b4690a; - --bad: #c0392b; - --shadow-lg: 0 26px 70px -34px rgba(60, 42, 16, 0.38); - --shadow-md: 0 16px 38px -22px rgba(60, 42, 16, 0.3); - --paper-grain: 0.5; + /* Clean paper — cool near-white, ink near-black */ + --bg: #f7f9fd; + --bg-2: #eef2f9; + --surface: #ffffff; + --surface-2: #f9fbfe; + --border: #e3e8f2; + --border-2: #ccd6e6; + --text: #1b2230; + --text-strong: #0b0e14; + --sub: #495264; + --muted: #6b7384; + --faint: #97a0b2; + --ok: #16a34a; + --info: #2563ff; + --warn: #06b6d4; + --bad: #e5484d; + --shadow-lg: 0 28px 64px -30px rgba(15, 30, 70, 0.3); + --shadow-md: 0 16px 38px -22px rgba(15, 30, 70, 0.22); + --paper-grain: 0; - /* fumadocs remap → Case File light */ - --color-fd-background: #f4ead4; - --color-fd-foreground: #2a2114; - --color-fd-muted: #ecdfc2; - --color-fd-muted-foreground: #6a5c3d; - --color-fd-popover: #fbf5e7; - --color-fd-popover-foreground: #17110a; - --color-fd-card: #fbf5e7; - --color-fd-card-foreground: #17110a; - --color-fd-border: #ddccab; - --color-fd-primary: #9e530e; - --color-fd-primary-foreground: #fbf5e7; - --color-fd-secondary: #efe3ca; - --color-fd-secondary-foreground: #2a2114; - --color-fd-accent: #ecdfc2; - --color-fd-accent-foreground: #17110a; - --color-fd-ring: #9e530e; + /* fumadocs remap → bright light */ + --color-fd-background: #f7f9fd; + --color-fd-foreground: #1b2230; + --color-fd-muted: #eef2f9; + --color-fd-muted-foreground: #6b7384; + --color-fd-popover: #ffffff; + --color-fd-popover-foreground: #0b0e14; + --color-fd-card: #ffffff; + --color-fd-card-foreground: #0b0e14; + --color-fd-border: #e3e8f2; + --color-fd-primary: #2563ff; + --color-fd-primary-foreground: #ffffff; + --color-fd-secondary: #eef2f9; + --color-fd-secondary-foreground: #1b2230; + --color-fd-accent: #e8eeff; + --color-fd-accent-foreground: #0b0e14; + --color-fd-ring: #2563ff; } .dark { - /* Night Stakeout — warm noir, not a cold terminal */ - --bg: #1b1610; - --bg-2: #221b12; - --surface: #2a2116; - --surface-2: #32281a; - --border: #43361f; - --border-2: #5e4b2c; - --text: #ece1cd; - --text-strong: #fff8ec; - --sub: #c3b094; - --muted: #9c8a6a; - --faint: #6f6045; + /* Cool slate complement — clean dark, not a warm noir, not an AI terminal */ + --bg: #0b0f17; + --bg-2: #121826; + --surface: #151c2b; + --surface-2: #1b2336; + --border: #283146; + --border-2: #3a4358; + --text: #e6ebf3; + --text-strong: #ffffff; + --sub: #aeb6c4; + --muted: #7f8799; + --faint: #596173; - /* brighter amber + red read better on the warm-dark surfaces */ - --accent: #e0934a; - --accent-2: #cf7a2a; - --accent-3: #f0aa63; - --grad: linear-gradient(120deg, #f0aa63 0%, #e0934a 60%, #cf7a2a 120%); - --grad-soft: linear-gradient(120deg, #f0aa63, #e0934a); - --accent-glow: rgba(224, 147, 74, 0.45); - --accent-weak: rgba(224, 147, 74, 0.14); - --accent-line: rgba(224, 147, 74, 0.32); - --warm: #e0934a; - --warm-2: #f0aa63; - --warm-ink: #2a1605; - --warm-glow: rgba(224, 147, 74, 0.45); - --warm-weak: rgba(224, 147, 74, 0.16); - --warm-line: rgba(224, 147, 74, 0.36); - --red: #e06a5c; - --red-weak: rgba(224, 106, 92, 0.16); - --ok: #5cc05f; - --info: #56b6e8; - --warn: #e0a341; - --bad: #e06a5c; - --shadow-lg: 0 26px 70px -34px rgba(0, 0, 0, 0.72); - --shadow-md: 0 16px 44px -26px rgba(0, 0, 0, 0.66); - --paper-grain: 0.7; + /* brighter blue + cyan + coral on the slate surfaces */ + --accent: #5a8cff; + --accent-2: #3b6ef0; + --accent-3: #34d6e8; + --glint: #34d6e8; + --grad: linear-gradient(120deg, #34d6e8 0%, #5a8cff 58%, #3b6ef0 120%); + --grad-soft: linear-gradient(120deg, #6f9bff, #5a8cff); + --accent-glow: rgba(90, 140, 255, 0.45); + --accent-weak: rgba(90, 140, 255, 0.16); + --accent-line: rgba(90, 140, 255, 0.32); + --warm: #5a8cff; + --warm-2: #4f86ff; + --warm-ink: #06122a; + --warm-glow: rgba(90, 140, 255, 0.5); + --warm-weak: rgba(90, 140, 255, 0.18); + --warm-line: rgba(90, 140, 255, 0.36); + --pop: #ff6b70; + --pop-weak: rgba(255, 107, 112, 0.16); + --pop-line: rgba(255, 107, 112, 0.4); + --red: #ff6b6b; + --red-weak: rgba(255, 107, 107, 0.16); + --ok: #3ecf7e; + --info: #5a8cff; + --warn: #34d6e8; + --bad: #ff6b6b; + --shadow-lg: 0 28px 64px -30px rgba(0, 0, 0, 0.72); + --shadow-md: 0 16px 44px -26px rgba(0, 0, 0, 0.62); + --paper-grain: 0; - /* fumadocs remap → night stakeout */ - --color-fd-background: #1b1610; - --color-fd-foreground: #ece1cd; - --color-fd-muted: #2a2116; - --color-fd-muted-foreground: #c3b094; - --color-fd-popover: #221b12; - --color-fd-popover-foreground: #ece1cd; - --color-fd-card: #2a2116; - --color-fd-card-foreground: #ece1cd; - --color-fd-border: #43361f; - --color-fd-primary: #e0934a; - --color-fd-primary-foreground: #1b1610; - --color-fd-secondary: #32281a; - --color-fd-secondary-foreground: #ece1cd; - --color-fd-accent: #322619; - --color-fd-accent-foreground: #fff8ec; - --color-fd-ring: #e0934a; + /* fumadocs remap → cool dark */ + --color-fd-background: #0b0f17; + --color-fd-foreground: #e6ebf3; + --color-fd-muted: #151c2b; + --color-fd-muted-foreground: #aeb6c4; + --color-fd-popover: #121826; + --color-fd-popover-foreground: #e6ebf3; + --color-fd-card: #151c2b; + --color-fd-card-foreground: #e6ebf3; + --color-fd-border: #283146; + --color-fd-primary: #5a8cff; + --color-fd-primary-foreground: #06122a; + --color-fd-secondary: #1b2336; + --color-fd-secondary-foreground: #e6ebf3; + --color-fd-accent: #1c2740; + --color-fd-accent-foreground: #ffffff; + --color-fd-ring: #5a8cff; +} + +/* ════════════════════════════════════════════════════════════════════════ + Theme snap — toggling the theme fires a quick white "screenshot" flash that + masks an instant swap, like snapping a photo of the repo (see snapToTheme in + lib/themeSnap.ts). One fading overlay, GPU-cheap — no full-page snapshot, so + no lag. Reduced-motion skips the flash (snapToTheme does a plain instant flip). + ──────────────────────────────────────────────────────────────────────── */ +.theme-flash { + position: fixed; + inset: 0; + z-index: 9999; + pointer-events: none; + background: #ffffff; + opacity: 0; + will-change: opacity; +} +.theme-flash.is-on { + animation: theme-snap 440ms ease-out forwards; +} +@keyframes theme-snap { + 0% { + opacity: 0; + } + 16% { + opacity: 0.92; + } + 100% { + opacity: 0; + } } diff --git a/website/app/icon.svg b/website/app/icon.svg index a6ac4a8..f982ffc 100644 --- a/website/app/icon.svg +++ b/website/app/icon.svg @@ -1,6 +1,6 @@ diff --git a/website/components/home/Hero.tsx b/website/components/home/Hero.tsx index 9e3fa61..f3d4643 100644 --- a/website/components/home/Hero.tsx +++ b/website/components/home/Hero.tsx @@ -8,13 +8,13 @@ export function Hero() {
diff --git a/website/components/home/HeroMascot.tsx b/website/components/home/HeroMascot.tsx index 2ed9697..a4c9339 100644 --- a/website/components/home/HeroMascot.tsx +++ b/website/components/home/HeroMascot.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; // Static assets live in public/ and are NOT auto-prefixed with the GitHub // Pages basePath the way next/link is — so we prefix the
The default choice for Node HTTP services; boring in the best way.
+
+ Express is the “hello world” of Node web servers. You hand it routes — “when someone
+ visits /users, run this function” — and it handles the plumbing in between.
+
+ Minimal on purpose: no database, no folder structure, no opinions. Just the + request-in / response-out basics, and a way to stack little functions in the middle. +
+ (req, res, next) functions you compose yourself.
+ http module; ~16k LOC,
+ no framework runtime underneath.
+ Permissive MIT license, active maintenance, no critical advisories in the manifest.
+