feat(website): redesign the mobile experience

Replace the stacked desktop layout with a phone-first product flow featuring an image-led hero, inline privacy proof, compact semantic search, swipeable feature cards with live pagination, expandable technical details, and touch-friendly navigation and calls to action. Add the standalone Phokus aperture SVG asset.
This commit is contained in:
2026-06-15 21:15:18 +01:00
parent e14dbda41d
commit 00bf7da344
3 changed files with 362 additions and 38 deletions
+43
View File
@@ -0,0 +1,43 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256" role="img" aria-label="Phokus">
<title>Phokus</title>
<!--
Phokus app mark — a camera iris.
The hexagon in the middle is the lens OPENING; each blade edge sweeps from a
corner of the opening out to the rim, all leaning the same way (the pinwheel).
Tuning knobs (edit and re-open in any browser/Figma):
* opening roundness -> the "52" radius in the opening <path> arcs.
smaller (toward 30) = rounder/softer hole; larger (toward 90) = flatter, sharper.
* blade curve -> the "110" radius in each blade <path>.
smaller = more pronounced curve; larger = straighter blade.
* blade sweep amount -> the +40deg baked into the blade endpoint (70.71,-84.27).
* curve DIRECTION -> the final flag in each "A r r 0 0 X" command (0<->1 flips the bow).
* weight -> stroke-width on the <g>.
* color -> stroke on the <g>; swap "#e9e9ec" for currentColor to inherit.
A brand-accent focal dot is provided at the bottom (commented out).
-->
<rect x="8" y="8" width="240" height="240" rx="52" fill="#0e0f14"/>
<g transform="translate(128 128)" fill="none" stroke="#e9e9ec" stroke-width="9"
stroke-linecap="round" stroke-linejoin="round">
<!-- outer rim -->
<circle r="110"/>
<!-- lens opening: soft, arc-rounded hexagon -->
<path d="M0,-46 A52,52 0 0 0 39.84,-23 A52,52 0 0 0 39.84,23 A52,52 0 0 0 0,46 A52,52 0 0 0 -39.84,23 A52,52 0 0 0 -39.84,-23 A52,52 0 0 0 0,-46 Z"/>
<!-- blades: one curved edge, swept six times -->
<g transform="rotate(0)"><path d="M0,-46 A110,110 0 0 1 70.71,-84.27"/></g>
<g transform="rotate(60)"><path d="M0,-46 A110,110 0 0 1 70.71,-84.27"/></g>
<g transform="rotate(120)"><path d="M0,-46 A110,110 0 0 1 70.71,-84.27"/></g>
<g transform="rotate(180)"><path d="M0,-46 A110,110 0 0 1 70.71,-84.27"/></g>
<g transform="rotate(240)"><path d="M0,-46 A110,110 0 0 1 70.71,-84.27"/></g>
<g transform="rotate(300)"><path d="M0,-46 A110,110 0 0 1 70.71,-84.27"/></g>
</g>
<!-- optional brand focal point -->
<!-- <circle cx="128" cy="128" r="9" fill="#e6a23c"/> -->
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+311 -37
View File
@@ -1,3 +1,4 @@
import { useRef, useState } from "react";
import { PhokusMark } from "./components/PhokusMark";
// Screenshots are 2560×1600 masters; imagetools downscales + transcodes to
// AVIF/WebP at build (see the Shot helper). `as=picture` must be the last query
@@ -58,10 +59,10 @@ function Shot({
function Header() {
return (
<header className="sticky top-0 z-50 border-b border-edge bg-canvas/70 backdrop-blur-md">
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-6">
<div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-5 lg:h-16 lg:px-6">
<a href="#top" className="flex items-center gap-2.5 text-ink">
<PhokusMark className="h-6 w-6 text-amber" />
<span className="font-display text-lg font-semibold tracking-tight">Phokus</span>
<PhokusMark className="h-5 w-5 text-amber lg:h-6 lg:w-6" />
<span className="font-display font-semibold tracking-tight lg:text-lg">Phokus</span>
</a>
<nav className="hidden items-center gap-8 text-sm text-muted md:flex">
<a href="#search" className="transition-colors hover:text-ink">Search</a>
@@ -71,21 +72,133 @@ function Header() {
</nav>
<a
href={DOWNLOAD_URL}
className="rounded-lg border border-edge-strong px-4 py-2 text-sm font-medium text-ink transition-colors hover:bg-surface"
className="hidden rounded-lg border border-edge-strong px-4 py-2 text-sm font-medium text-ink transition-colors hover:bg-surface md:block"
>
Download
</a>
<a
href={REPO_URL}
aria-label="View Phokus source on GitHub"
className="grid h-9 w-9 place-items-center rounded-full border border-edge-strong text-muted transition-colors hover:bg-surface hover:text-ink md:hidden"
>
<GitHubIcon className="h-4 w-4" />
</a>
</div>
</header>
);
}
function MobileChapterNav() {
return (
<nav
aria-label="Page sections"
className="sticky top-14 z-40 flex gap-1 overflow-x-auto border-b border-edge bg-canvas/90 px-4 py-2 backdrop-blur-md lg:hidden"
>
{[
["Search", "#search"],
["Features", "#features"],
["Details", "#tech"],
["Download", "#download"],
].map(([label, href]) => (
<a
key={href}
href={href}
className="shrink-0 rounded-full px-3 py-1.5 text-xs font-medium text-muted transition-colors hover:bg-surface-2 hover:text-ink"
>
{label}
</a>
))}
</nav>
);
}
function MobileChapter({
number,
label,
title,
children,
}: {
number: string;
label: string;
title: string;
children: React.ReactNode;
}) {
return (
<div className="lg:hidden">
<div className="flex items-center gap-3 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-faint">
<span className="font-mono text-amber">{number}</span>
<span className="h-px w-8 bg-edge-strong" />
{label}
</div>
<h2 className="mt-3 max-w-[20rem] font-display text-[1.75rem] font-semibold leading-[1.08] tracking-tight text-ink">
{title}
</h2>
{children}
</div>
);
}
function Hero() {
return (
<section id="top" className="relative overflow-hidden lg:flex lg:min-h-[86vh] lg:items-center">
<section id="top" className="relative overflow-hidden">
<div className="lg:hidden">
<div className="relative h-[34svh] min-h-64 overflow-hidden border-b border-edge">
<Shot
pic={heroShot}
alt="The Phokus gallery showing a dense library of landscape photos."
className="absolute inset-0 h-full w-full object-cover object-[67%_top]"
/>
<div className="absolute inset-0 bg-gradient-to-b from-canvas/20 via-transparent to-canvas" />
<div className="absolute inset-x-5 top-5 flex items-center justify-between">
<span className="rounded-full border border-white/10 bg-black/45 px-3 py-1.5 text-[0.68rem] font-medium text-white/80 backdrop-blur">
Your folders. Your machine.
</span>
<span className="flex items-center gap-1.5 text-[0.68rem] text-white/60">
<span className="h-1.5 w-1.5 rounded-full bg-amber" />
Windows
</span>
</div>
</div>
<div className="relative -mt-7 px-5 pb-12">
<h1 className="max-w-[21rem] font-display text-[2.45rem] font-semibold leading-[0.98] tracking-[-0.035em] text-ink">
See everything.
<br />
Find anything.
</h1>
<p className="mt-4 max-w-[22rem] text-[0.95rem] leading-6 text-muted">
Search and curate the images and videos already on your computer. Everything stays local.
</p>
<div className="mt-6">
<a
href={DOWNLOAD_URL}
className="flex min-h-12 items-center justify-between rounded-xl bg-amber px-5 text-sm font-semibold text-canvas transition-colors hover:bg-amber-soft"
>
Download for Windows
<span aria-hidden="true"></span>
</a>
</div>
<div className="mt-5 grid grid-cols-3 divide-x divide-edge border-y border-edge py-4 text-center">
<span className="px-2 text-[0.68rem] leading-4 text-faint">
<strong className="block font-medium text-ink">Local</strong>
no uploads
</span>
<span className="px-2 text-[0.68rem] leading-4 text-faint">
<strong className="block font-medium text-ink">Private</strong>
no account
</span>
<span className="px-2 text-[0.68rem] leading-4 text-faint">
<strong className="block font-medium text-ink">Offline</strong>
after setup
</span>
</div>
</div>
</div>
<div className="relative hidden min-h-[86vh] items-center lg:flex">
{/* Product leads: the gallery dominates the right and bleeds off the frame,
cropped so the workspace appears to continue beyond the viewport. */}
<div className="pointer-events-none absolute inset-y-0 right-0 hidden w-[62%] lg:block">
<div className="pointer-events-none absolute inset-y-0 right-0 w-[62%]">
<Shot
pic={heroShot}
alt=""
@@ -97,13 +210,13 @@ function Hero() {
</div>
<div className="pointer-events-none absolute -left-24 top-8 -z-10 h-96 w-96 rounded-full bg-amber/[0.06] blur-3xl" />
<div className="relative mx-auto w-full max-w-7xl px-6 py-16 sm:py-20 lg:py-24">
<div className="relative mx-auto w-full max-w-7xl px-6 py-24">
<div className="max-w-2xl">
<p className="mb-6 inline-flex items-center gap-2 rounded-full border border-edge bg-surface/80 px-3 py-1 text-xs font-medium text-muted backdrop-blur">
<span className="h-1.5 w-1.5 rounded-full bg-amber" />
Local-first · Windows desktop
</p>
<h1 className="font-display text-[2.5rem] font-semibold leading-[1.06] tracking-tight text-ink sm:text-6xl sm:leading-[1.04]">
<h1 className="font-display text-6xl font-semibold leading-[1.04] tracking-tight text-ink">
Your local media library,
<br />
made searchable.
@@ -132,14 +245,6 @@ function Hero() {
</p>
</div>
</div>
{/* Mobile/tablet: product sits below the copy, still bleeding off the right. */}
<div className="-mr-6 mt-2 overflow-hidden rounded-l-xl border-y border-l border-edge lg:hidden">
<Shot
pic={heroShot}
alt="The Phokus gallery: a virtualized grid of landscape photos with star ratings, a folder sidebar, filter tabs, and a search bar."
className="block w-full"
/>
</div>
</section>
);
@@ -161,9 +266,9 @@ function LocalFirst() {
},
];
return (
<section id="privacy" className="relative overflow-hidden border-y border-edge bg-surface">
<section id="privacy" className="relative hidden overflow-hidden border-y border-edge bg-surface lg:block">
<EdgeMark side="left" />
<div className="relative mx-auto max-w-7xl px-6 py-20 sm:py-28">
<div className="relative mx-auto max-w-7xl px-6 py-28">
<div className="max-w-2xl">
<SectionEyebrow label="Local-first" />
<h2 className="mt-4 font-display text-3xl font-semibold tracking-tight text-ink sm:text-4xl">
@@ -200,7 +305,31 @@ function SearchSection() {
return (
<section id="search" className="relative overflow-hidden">
<EdgeMark side="right" />
<div className="relative mx-auto max-w-7xl px-6 py-20 sm:py-28">
<div className="relative mx-auto max-w-7xl px-5 py-12 lg:px-6 lg:py-28">
<MobileChapter number="02" label="Semantic search" title="Describe the image. Skip the filename.">
<div className="mt-6 flex min-h-11 items-center gap-3 rounded-xl border border-edge-strong bg-surface px-4 shadow-lg shadow-black/20">
<span className="rounded bg-surface-2 px-1.5 py-0.5 font-mono text-xs text-amber">/s</span>
<span className="text-sm text-ink">seaside</span>
<span className="ml-auto text-xs text-faint">200 matches</span>
</div>
<div className="relative mt-3 h-80 overflow-hidden rounded-2xl border border-edge bg-surface shadow-2xl shadow-black/40">
<Shot
pic={semanticShot}
alt="Coastal photos returned for a semantic search for seaside."
className="h-full w-full object-cover object-[65%_center]"
/>
<div className="absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-black/80 to-transparent" />
<p className="absolute bottom-4 left-4 right-4 text-xs leading-5 text-white/70">
Coastlines, waves, shells and dunes, matched by visual meaning.
</p>
</div>
<p className="mt-5 text-sm leading-6 text-muted">
Search by mood, place, subject, or visual idea. CLIP runs on-device, so it still works
offline and needs no manual tags.
</p>
</MobileChapter>
<div className="hidden lg:block">
<div className="max-w-2xl">
<SectionEyebrow label="Search by meaning" />
<h2 className="mt-4 font-display text-3xl font-semibold tracking-tight text-ink sm:text-4xl">
@@ -223,6 +352,131 @@ function SearchSection() {
className="block w-full"
/>
</figure>
</div>
</div>
</section>
);
}
function MobileFeatureDeck() {
const [activeCard, setActiveCard] = useState(0);
const cardRefs = useRef<Array<HTMLElement | null>>([]);
function updateActiveCard(event: React.UIEvent<HTMLDivElement>) {
const deckCenter = event.currentTarget.scrollLeft + event.currentTarget.clientWidth / 2;
let nearestIndex = 0;
let nearestDistance = Number.POSITIVE_INFINITY;
cardRefs.current.forEach((card, index) => {
if (!card) return;
const cardCenter = card.offsetLeft + card.offsetWidth / 2;
const distance = Math.abs(cardCenter - deckCenter);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestIndex = index;
}
});
setActiveCard(nearestIndex);
}
function showCard(index: number) {
cardRefs.current[index]?.scrollIntoView({
behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth",
block: "nearest",
inline: "center",
});
}
return (
<section id="features" className="overflow-hidden border-y border-edge bg-surface py-12 lg:hidden">
<div className="px-5">
<MobileChapter number="03" label="More than search" title="Three ways to move through a library.">
<p className="mt-4 text-sm leading-6 text-muted">
Swipe through the core workflows. Each one stays focused on the media, not app chrome.
</p>
</MobileChapter>
</div>
<div
onScroll={updateActiveCard}
className="mt-7 flex snap-x snap-mandatory gap-3 overflow-x-auto px-5 pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
>
<article
ref={(node) => { cardRefs.current[0] = node; }}
className="w-[78vw] max-w-[19rem] shrink-0 snap-center overflow-hidden rounded-2xl border border-edge bg-canvas"
>
<div className="h-64 overflow-hidden">
<Shot
pic={exploreShot}
alt="Visual clusters arranged across the Explore canvas."
className="h-full w-full object-cover object-[54%_center]"
/>
</div>
<div className="p-4">
<span className="font-mono text-[0.65rem] text-amber">EXPLORE</span>
<h3 className="mt-2 font-display text-xl font-semibold text-ink">See visual clusters.</h3>
<p className="mt-2 text-sm leading-6 text-muted">
Wander through related shots, then switch to a chronological timeline.
</p>
</div>
</article>
<article
ref={(node) => { cardRefs.current[1] = node; }}
className="w-[78vw] max-w-[19rem] shrink-0 snap-center overflow-hidden rounded-2xl border border-edge bg-canvas"
>
<div className="h-64 overflow-hidden">
<Shot
pic={lightboxShot}
alt="A waterfall open in the Phokus lightbox."
className="h-full w-full object-cover object-[37%_center]"
/>
</div>
<div className="p-4">
<span className="font-mono text-[0.65rem] text-amber">CURATE</span>
<h3 className="mt-2 font-display text-xl font-semibold text-ink">Review without leaving.</h3>
<p className="mt-2 text-sm leading-6 text-muted">
Rate, favourite, tag, zoom, play video, and find similar images in place.
</p>
</div>
</article>
<article
ref={(node) => { cardRefs.current[2] = node; }}
className="w-[78vw] max-w-[19rem] shrink-0 snap-center overflow-hidden rounded-2xl border border-edge bg-canvas"
>
<div className="h-64 overflow-hidden">
<Shot
pic={duplicatesShot}
alt="Duplicate image pairs selected for safe cleanup."
className="h-full w-full object-cover object-[24%_top]"
/>
</div>
<div className="p-4">
<span className="font-mono text-[0.65rem] text-amber">CLEANUP</span>
<h3 className="mt-2 font-display text-xl font-semibold text-ink">Compare exact pairs.</h3>
<p className="mt-2 text-sm leading-6 text-muted">
Confirm byte-identical files and choose what to remove. Nothing is automatic.
</p>
</div>
</article>
</div>
<div className="flex items-center gap-2 px-5 text-[0.68rem] text-faint">
{[0, 1, 2].map((index) => (
<button
key={index}
type="button"
aria-label={`Show feature ${index + 1}`}
aria-current={activeCard === index ? "true" : undefined}
onClick={() => showCard(index)}
className={`h-1.5 rounded-full transition-[width,background-color] ${
activeCard === index ? "w-8 bg-amber" : "w-1.5 bg-edge-strong"
}`}
/>
))}
<span className="ml-1">Swipe</span>
</div>
</section>
);
@@ -271,9 +525,9 @@ function EdgeMark({ side }: { side: "left" | "right" }) {
function ExploreSection() {
return (
<section id="explore" className="relative overflow-hidden border-t border-edge bg-surface">
<section id="explore" className="relative hidden overflow-hidden border-t border-edge bg-surface lg:block">
<EdgeMark side="left" />
<div className="relative mx-auto max-w-7xl px-6 py-20 sm:py-28">
<div className="relative mx-auto max-w-7xl px-5 py-16 lg:px-6 lg:py-28">
<div className="grid items-center gap-10 lg:grid-cols-12">
<div className="lg:col-span-5">
<SectionEyebrow label="Explore & timeline" />
@@ -308,9 +562,9 @@ function ExploreSection() {
function CurateSection() {
return (
<section id="curate" className="relative overflow-hidden">
<section id="curate" className="relative hidden overflow-hidden lg:block">
<EdgeMark side="right" />
<div className="relative mx-auto max-w-7xl px-6 py-20 sm:py-28">
<div className="relative mx-auto max-w-7xl px-5 py-16 lg:px-6 lg:py-28">
<div className="grid items-center gap-10 lg:grid-cols-12">
<figure className="order-2 overflow-hidden rounded-xl border border-edge bg-surface shadow-2xl shadow-black/50 lg:order-1 lg:col-span-7">
<Shot
@@ -345,9 +599,9 @@ function CurateSection() {
function CleanupSection() {
return (
<section id="cleanup" className="relative overflow-hidden border-t border-edge bg-surface">
<section id="cleanup" className="relative hidden overflow-hidden border-t border-edge bg-surface lg:block">
<EdgeMark side="left" />
<div className="relative mx-auto max-w-7xl px-6 py-20 sm:py-28">
<div className="relative mx-auto max-w-7xl px-5 py-16 lg:px-6 lg:py-28">
<div className="max-w-2xl">
<SectionEyebrow label="Clean up safely" />
<h2 className="mt-4 font-display text-3xl font-semibold tracking-tight text-ink sm:text-4xl">
@@ -381,7 +635,23 @@ function TechFactsSection() {
{ k: "License & build", v: "MIT, open source. The installer is currently unsigned — see the note below." },
];
return (
<section id="tech" className="relative mx-auto max-w-7xl px-6 py-20 sm:py-28">
<section id="tech" className="relative mx-auto max-w-7xl px-5 py-12 lg:px-6 lg:py-28">
<MobileChapter number="04" label="Technical details" title="The practical bits.">
<div className="mt-6 divide-y divide-edge border-y border-edge">
{facts.map((fact, index) => (
<details key={fact.k} className="group py-1">
<summary className="flex min-h-12 cursor-pointer list-none items-center gap-3 py-2 text-sm font-medium text-ink">
<span className="font-mono text-[0.68rem] text-faint">0{index + 1}</span>
{fact.k}
<span className="ml-auto text-lg font-light text-amber transition-transform group-open:rotate-45">+</span>
</summary>
<p className="pb-5 pl-8 text-sm leading-6 text-muted">{fact.v}</p>
</details>
))}
</div>
</MobileChapter>
<div className="hidden lg:block">
<div className="max-w-2xl">
<SectionEyebrow label="The technical facts" />
<h2 className="mt-4 font-display text-3xl font-semibold tracking-tight text-ink sm:text-4xl">
@@ -396,6 +666,7 @@ function TechFactsSection() {
</div>
))}
</dl>
</div>
</section>
);
}
@@ -404,30 +675,31 @@ function DownloadSection() {
return (
<section id="download" className="relative overflow-hidden border-t border-edge bg-surface">
<EdgeMark side="right" />
<div className="relative mx-auto max-w-3xl px-6 py-20 text-center sm:py-28">
<PhokusMark className="mx-auto h-12 w-12 text-ink" dotClassName="fill-amber" />
<h2 className="mt-6 font-display text-3xl font-semibold tracking-tight text-ink sm:text-4xl">
<div className="relative mx-auto max-w-3xl px-5 py-12 text-left lg:px-6 lg:py-28 lg:text-center">
<PhokusMark className="h-10 w-10 text-ink lg:mx-auto lg:h-12 lg:w-12" dotClassName="fill-amber" />
<h2 className="mt-6 max-w-xs font-display text-[2rem] font-semibold leading-tight tracking-tight text-ink lg:mx-auto lg:max-w-none lg:text-4xl">
Point Phokus at a folder.
</h2>
<p className="mt-4 text-lg leading-relaxed text-muted">
Free and open source. Your library is indexed on your machine — the download is just the app.
<p className="mt-4 max-w-md text-base leading-7 text-muted lg:mx-auto lg:text-lg lg:leading-relaxed">
Turn a folder full of files into a library you can actually explore.
</p>
<div className="mt-8 flex flex-wrap justify-center gap-3">
<div className="mt-8 grid gap-3 lg:flex lg:flex-wrap lg:justify-center">
<a
href={DOWNLOAD_URL}
className="rounded-lg bg-amber px-6 py-3 text-sm font-medium text-canvas transition-colors hover:bg-amber-soft"
className="flex min-h-12 items-center justify-between rounded-xl bg-amber px-5 text-sm font-semibold text-canvas transition-colors hover:bg-amber-soft lg:min-h-0 lg:justify-center lg:rounded-lg lg:px-6 lg:py-3 lg:font-medium"
>
Download for Windows
<span className="lg:hidden" aria-hidden="true">↓</span>
</a>
<a
href={REPO_URL}
className="inline-flex items-center gap-2 rounded-lg border border-edge-strong px-6 py-3 text-sm font-medium text-ink transition-colors hover:bg-surface-2"
className="flex min-h-12 items-center justify-between rounded-xl border border-edge-strong px-5 text-sm font-medium text-ink transition-colors hover:bg-surface-2 lg:min-h-0 lg:justify-center lg:gap-2 lg:rounded-lg lg:px-6 lg:py-3"
>
<GitHubIcon className="h-4 w-4" />
View source
<GitHubIcon className="h-4 w-4" />
</a>
</div>
<p className="mx-auto mt-10 max-w-xl rounded-lg border border-edge bg-canvas/60 p-4 text-left text-sm leading-relaxed text-muted">
<p className="mt-8 max-w-xl border-l border-amber/50 pl-4 text-left text-xs leading-5 text-muted lg:mx-auto lg:mt-10 lg:rounded-lg lg:border lg:border-edge lg:bg-canvas/60 lg:p-4 lg:text-sm lg:leading-relaxed">
<span className="font-medium text-ink">Heads up:</span> this build isn't code-signed yet, so
Windows SmartScreen will warn that the publisher is unrecognised choose{" "}
<span className="text-ink">More info Run anyway</span>. An NVIDIA CUDA build is on the
@@ -441,7 +713,7 @@ function DownloadSection() {
function Footer() {
return (
<footer>
<div className="mx-auto flex max-w-7xl flex-col gap-4 px-6 py-10 text-sm text-faint sm:flex-row sm:items-center sm:justify-between">
<div className="mx-auto flex max-w-7xl flex-col gap-5 px-5 py-8 text-xs text-faint lg:flex-row lg:items-center lg:justify-between lg:px-6 lg:py-10 lg:text-sm">
<div className="flex items-center gap-2 text-muted">
<PhokusMark className="h-5 w-5 text-amber" />
<span className="font-display font-medium text-ink">Phokus</span>
@@ -462,10 +734,12 @@ export function App() {
return (
<div className="min-h-screen">
<Header />
<MobileChapterNav />
<main>
<Hero />
<LocalFirst />
<SearchSection />
<MobileFeatureDeck />
<ExploreSection />
<CurateSection />
<CleanupSection />
+8 -1
View File
@@ -47,11 +47,18 @@ body {
text-rendering: optimizeLegibility;
}
a:focus-visible {
a:focus-visible,
summary:focus-visible {
outline: 2px solid var(--color-amber);
outline-offset: 4px;
}
@media (max-width: 63.999rem) {
section[id] {
scroll-margin-top: 6.25rem;
}
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;