Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export default async function BlogPost({
<Link
href="/blog/"
aria-label="Back to field notes"
className="meta inline-flex items-center gap-2 self-start opacity-50 no-underline transition-opacity hover:opacity-100"
className="meta inline-flex items-center gap-2 self-start opacity-60 no-underline transition-opacity hover:opacity-100"
>
<span aria-hidden>←</span> field notes
</Link>
Expand All @@ -121,7 +121,7 @@ export default async function BlogPost({
notes" back-link above stays flush-left as nav chrome. */}
<header className="flex flex-col items-center gap-6 text-center">
<div
className={`${META} flex flex-wrap items-center justify-center gap-x-3 gap-y-1 tabular-nums opacity-50`}
className={`${META} flex flex-wrap items-center justify-center gap-x-3 gap-y-1 tabular-nums opacity-60`}
>
<span>§ {num}</span>
<span aria-hidden>·</span>
Expand Down Expand Up @@ -168,10 +168,10 @@ export default async function BlogPost({
{/* The title holds steady on hover — the glyph slides +
greens and the label lifts, mirroring the index row's
`→`. */}
<span className="meta opacity-50 transition-opacity duration-300 ease-out group-hover:opacity-90">
<span className="meta opacity-60 transition-opacity duration-300 ease-out group-hover:opacity-90">
<span
aria-hidden
className="inline-block transition-all duration-300 ease-out group-hover:-translate-x-1 group-hover:text-whisper"
className="inline-block transition-[translate,color] duration-300 ease-out group-hover:-translate-x-1 group-hover:text-whisper"
>
←
</span>{" "}
Expand All @@ -189,11 +189,11 @@ export default async function BlogPost({
href={`/blog/${newer.slug}/`}
className="group flex flex-col gap-2 no-underline @xl:items-end @xl:text-right"
>
<span className="meta opacity-50 transition-opacity duration-300 ease-out group-hover:opacity-90">
<span className="meta opacity-60 transition-opacity duration-300 ease-out group-hover:opacity-90">
later{" "}
<span
aria-hidden
className="inline-block transition-all duration-300 ease-out group-hover:translate-x-1 group-hover:text-whisper"
className="inline-block transition-[translate,color] duration-300 ease-out group-hover:translate-x-1 group-hover:text-whisper"
>
→
</span>
Expand Down
2 changes: 1 addition & 1 deletion app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export default function BlogIndex() {
<div className="mt-16 flex flex-col">
<div
aria-hidden
className={`meta hidden pb-3 opacity-50 @xl:grid ${BLOG_GRID_COLS}`}
className={`meta hidden pb-3 opacity-60 @xl:grid ${BLOG_GRID_COLS}`}
>
<span>#</span>
<span>date</span>
Expand Down
94 changes: 62 additions & 32 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
--ink: #000000;
--paper: #ffffff;
--whisper: #0f9d6a;
/* Raw --whisper is ~3.5:1 on paper (short of AA for text) and ~6:1 on
ink. --whisper-deep mixes it 68% with ink for ~6.5:1 on paper.
--whisper-text is what readable brand text uses (highlightBrand, via
`text-whisper-text`): raw by default, matching the body's ink ground,
and swapped to --whisper-deep by every Frame tone="paper". Decorative
glyphs and icons (prompt and arrow marks, the pin, the hero's full stop)
keep raw --whisper — ~3.5:1 clears the 3:1 non-text bar. */
--whisper-deep: color-mix(in srgb, var(--whisper) 68%, var(--ink));
--whisper-text: var(--whisper);
/* Intentionally 0: anchor jumps land flush at viewport top (under
the fixed nav). Section headers don't get clipped because every
Frame's --spacing-frame-pad-y floor (5rem = 80px) sits comfortably
Expand All @@ -25,6 +34,7 @@
--color-ink: var(--ink);
--color-paper: var(--paper);
--color-whisper: var(--whisper);
--color-whisper-text: var(--whisper-text);

--font-sans:
var(--font-geist-sans), ui-sans-serif, system-ui, -apple-system,
Expand Down Expand Up @@ -204,7 +214,6 @@ body {
opacity 800ms var(--ease-soft),
transform 800ms var(--ease-soft);
transition-delay: var(--reveal-delay, 0ms);
will-change: opacity, transform;
}

[data-reveal][data-revealed="true"] {
Expand Down Expand Up @@ -320,18 +329,18 @@ a.cta-primary:focus-visible {
outline-offset: 9px;
}

/* The status link under the hero terminal. Raw --whisper is ~3.5:1 on
paper, short of AA at body size, so the value text uses whisper mixed
68% with ink (~6.6:1). The label is ink like the stat labels; the dot
and the arrow keep the full accent. */
/* The status link under the hero terminal. The value text takes
--whisper-deep (see :root) for AA on paper — directly rather than via
--whisper-text, so the link holds AA even outside a paper Frame. The label
is ink like the stat labels; the dot and the arrow keep the full accent. */
a.hero-status {
color: color-mix(in srgb, var(--whisper) 68%, var(--ink));
color: var(--whisper-deep);
}

.hero-status-text {
padding-bottom: 1px;
border-bottom: 1px solid transparent;
transition: border-bottom-color 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
transition: border-bottom-color 260ms var(--ease-soft);
}

a.hero-status:hover .hero-status-text {
Expand All @@ -340,7 +349,7 @@ a.hero-status:hover .hero-status-text {

a.hero-status .arrow {
margin-right: 0;
transition: transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
transition: transform 260ms var(--ease-expo);
}

a.hero-status:hover .arrow {
Expand Down Expand Up @@ -370,8 +379,11 @@ a.hero-status:hover .arrow {
Replays recent on-chain settlements from the live stats file
(lib/stats.ts), one per cycle: tx → block → operator → bytes → paid.
All lines share one 4.5s loop; each line fades in at its own delay.
Progress bar fills during the "delivered" line. Cursor blinks
independently. The cascade restarts every cycle. Shares the "tool
Progress bar fills during the "delivered" line. HeroTerminal advances to
the next settlement on .tl-0's animationiteration, so anything that stops
the cascade stops the rotation: reduced motion and the offscreen pause
below. The cursor blinks independently except offscreen.
The cascade restarts every cycle. Shares the "tool
panel" vocabulary with .fleet and .faq-card: soft corners, a solid
1px rule, a hard offset shadow. */

Expand Down Expand Up @@ -485,7 +497,7 @@ a.hero-status:hover .arrow {
}

.terminal .dim {
opacity: 0.45;
opacity: 0.6;
}

.terminal .amount {
Expand All @@ -506,29 +518,30 @@ a.hero-status:hover .arrow {
.terminal .progress-fill {
position: absolute;
inset: 0;
width: 0%;
background: var(--whisper);
transform: scaleX(0);
transform-origin: left center;
animation: term-progress 4.5s linear infinite;
animation-delay: 1.1s;
}

@keyframes term-progress {
0%,
24% {
width: 0%;
transform: scaleX(0);
}
44% {
width: 100%;
transform: scaleX(1);
}
80% {
width: 100%;
transform: scaleX(1);
}
88% {
width: 100%;
transform: scaleX(1);
opacity: 0;
}
100% {
width: 0%;
transform: scaleX(0);
opacity: 0;
}
}
Expand Down Expand Up @@ -560,10 +573,15 @@ a.hero-status:hover .arrow {
transform: none;
}
.terminal .progress-fill {
width: 100%;
transform: none;
}
}

/* Set by HeroTerminal while the panel is scrolled out of view. */
[data-offscreen] .terminal :is(.tl, .progress-fill, .cursor) {
animation-play-state: paused;
}

/* --- fleet status panel (§05) -----------------------------------------
Sits on the paper Contact section as an inverted panel. Same "tool
panel" vocabulary as .terminal — soft corners, solid rule, hard
Expand Down Expand Up @@ -746,7 +764,8 @@ a.hero-status:hover .arrow {

/* Hourly spark strip under each aggregate: one cell per hour of the 24h
window, oldest left, each cell's height its share of the busiest hour
(--level, 0–1). A zero hour keeps a hairline so the strip reads as a
(--level, 0–1) — drawn as a scaleY of a full-height cell, so it animates
on the compositor. A zero hour keeps a hairline so the strip reads as a
timeline rather than a gap. */
.fleet-spark {
display: flex;
Expand All @@ -760,9 +779,13 @@ a.hero-status:hover .arrow {

.fleet-spark-cell {
flex: 1;
height: max(1px, calc(var(--level, 0) * 100%));
height: 100%;
background: var(--whisper);
transition: height 0.6s var(--ease-term);
/* 0.08 × the strip's 13px content box (14px height minus the 1px
border-bottom) ≈ the 1px hairline. Retune if either changes. */
transform: scaleY(max(0.08, var(--level, 0)));
transform-origin: bottom;
transition: transform 0.6s var(--ease-term);
}

@media (prefers-reduced-motion: reduce) {
Expand Down Expand Up @@ -1090,20 +1113,27 @@ a.hero-status:hover .arrow {
Phone-only (<md) slide-out drawer. See MobileMenu.tsx for the
portal/focus model. */

/* A 44px hit area around an 18px glyph. The box overhangs the gutter by
11px and insets the bars 13px, so the glyph sits 2px inside the gutter;
top: 13px centres it on the nav's ~70px bar (py-5 + 30px wordmark, see
:root). Retune if the nav height changes. */
.mm-toggle {
position: fixed;
top: 24px;
right: var(--spacing-frame-gutter);
top: 13px;
right: calc(var(--spacing-frame-gutter) - 11px);
z-index: 65;
width: 22px;
height: 22px;
width: 44px;
height: 44px;
color: var(--ink);
background: transparent;
border: 0;
padding: 0;
cursor: pointer;
transition: color 400ms var(--ease-soft);
}
.mm-toggle:focus-visible {
outline-offset: -6px;
}
.mm-toggle[data-tone="ink"] {
color: var(--paper);
}
Expand All @@ -1112,8 +1142,8 @@ a.hero-status:hover .arrow {
.mm-toggle::after {
content: "";
position: absolute;
left: 2px;
right: 2px;
left: 13px;
right: 13px;
height: 1px;
background: currentColor;
transition:
Expand All @@ -1122,18 +1152,18 @@ a.hero-status:hover .arrow {
}

.mm-toggle::before {
top: 8px;
top: 19px;
}
.mm-toggle::after {
top: 14px;
top: 25px;
}

.mm-toggle[data-open="true"]::before {
top: 11px;
top: 22px;
transform: rotate(45deg);
}
.mm-toggle[data-open="true"]::after {
top: 11px;
top: 22px;
transform: rotate(-45deg);
}

Expand Down Expand Up @@ -1195,7 +1225,7 @@ a.hero-status:hover .arrow {
}
.mm-head-link:hover,
.mm-head-link:active {
color: var(--whisper);
color: var(--whisper-deep);
}

.mm-list {
Expand Down
2 changes: 1 addition & 1 deletion components/site/FleetStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const EMPTY_LABEL = {
const FLAT = Array<number>(FLEET_WINDOW_HOURS).fill(0);

/** One hourly spark strip: a cell per hour, its height the hour's share of
* the window's busiest hour. */
* the window's busiest hour (drawn as a scaleY in globals.css). */
function Spark({ levels }: { levels: number[] }) {
return (
<div className="fleet-spark">
Expand Down
16 changes: 8 additions & 8 deletions components/site/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,40 @@ export function Footer() {
<footer className="bg-paper px-frame-gutter pb-10 text-ink">
<div className="@container mx-auto flex max-w-frame flex-col gap-3">
<span aria-hidden className="rule opacity-40" />
<div className="grid grid-cols-1 gap-6 text-micro tracking-[0.2em] uppercase opacity-80 @md:grid-cols-3 @md:items-start @md:gap-2">
<span>© decdn labs · open source</span>
<span className="@md:text-center">
<div className="grid grid-cols-1 gap-6 text-micro tracking-[0.2em] uppercase @md:grid-cols-3 @md:items-start @md:gap-2">
<span className="opacity-80">© decdn labs · open source</span>
<span className="opacity-80 @md:text-center">
built in rust · probably over-engineered
</span>
{/* Right-aligned vertical list: legal pages stacked above the
presskit download. */}
<nav
aria-label="Legal and resources"
className="flex flex-col gap-2 @md:items-end @md:justify-self-end"
className="-mt-1 flex flex-col @md:items-end @md:justify-self-end"
>
<Link
href={links.privacy}
className="no-underline transition-opacity hover:opacity-100"
className="py-1 underline-offset-4 opacity-80 transition-opacity hover:underline hover:opacity-100"
>
privacy
</Link>
<Link
href={links.terms}
className="no-underline transition-opacity hover:opacity-100"
className="py-1 underline-offset-4 opacity-80 transition-opacity hover:underline hover:opacity-100"
>
terms
</Link>
<Link
href={links.disclaimer}
className="no-underline transition-opacity hover:opacity-100"
className="py-1 underline-offset-4 opacity-80 transition-opacity hover:underline hover:opacity-100"
>
disclaimer
</Link>
<a
href={links.presskit}
download
aria-label="Download presskit"
className="mt-1 inline-flex items-baseline gap-2 border-b border-current pb-[2px] no-underline hover:opacity-100"
className="mt-2 inline-flex items-baseline gap-2 border-b border-current pb-[2px] no-underline opacity-80 transition-opacity hover:opacity-100"
>
<span>presskit</span>
<span aria-hidden>↓</span>
Expand Down
Loading
Loading