Merge nucleic/upbeat-jade-ibis-kt5e into main

Mirrored from corpo a08e349.
This commit is contained in:
2026-08-09 17:09:41 -07:00
parent 382f44167b
commit 17b8ffd69b
+40 -7
View File
@@ -9,11 +9,15 @@ const canonical = new URL('/', Astro.site);
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<!-- Deliberately no viewport-fit=cover. It looks like the fix for the bands behind
Safari's chrome and it is not: measured on iOS 27, screen.height is 874 while
100lvh is 754 and both safe-area insets report 0, cover or not. Safari does not
hand those 120px to the page, so no element and no viewport unit reaches them —
see the note on .ambient for what actually resolves it. -->
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<!-- Not --paper. Safari paints its toolbar and overscroll regions with this flat <!-- Not --paper. Safari paints its overscroll regions with this flat colour, but
colour, but the page underneath is the ambient field, which lifts the edges to the page underneath is the ambient field, which lifts the edges well off paper.
roughly #0e1813 — against paper that reads as a grey bar sitting behind the Measured off rendered pixels at the top and bottom edges. -->
browser chrome. Measured off rendered pixels at the top and bottom edges. -->
<meta name="theme-color" content="#0f1813" /> <meta name="theme-color" content="#0f1813" />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<title>{title}</title> <title>{title}</title>
@@ -173,14 +177,42 @@ const canonical = new URL('/', Astro.site);
/* Deliberately not inset: 0. A fixed box pinned to the bottom stops at the small /* Deliberately not inset: 0. A fixed box pinned to the bottom stops at the small
viewport, but iOS Safari keeps rendering the page behind its translucent viewport, but iOS Safari keeps rendering the page behind its translucent
toolbar — so the strip between svh and lvh fell back to flat paper and read as toolbar — so the strip between svh and lvh fell back to flat paper and read as
a grey bar under the chrome, several shades off the ambient-lit page above it. a grey bar under the chrome. Sized to the large viewport, the field paints
Sized to the large viewport, the field paints through the toolbar instead. */ through the toolbar instead. */
height: 100%; height: 100%;
height: 100lvh; height: 100lvh;
overflow: hidden; overflow: hidden;
pointer-events: none; pointer-events: none;
} }
/* The grey bar, properly.
iOS reserves a strip above and below the layout viewport for its own chrome, and
fills it from the document canvas — which takes its paint from the root element
and nowhere else. Measured on iOS 27: screen.height 874, 100lvh 754, both safe-area
insets 0. Those 120px are not addressable by the page. No height, no viewport unit
and no viewport-fit=cover reaches them; earlier attempts only moved the seam.
So the canvas is the single point, and there is exactly one thing to get right:
the canvas colour and the page's own edge colour have to be equal. The canvas can
only be flat --paper, and the field is a moving light whose edge tone drifts, so
matching by picking a constant is a value that is wrong again as soon as the bands
move. Instead the field is brought to zero at the bottom of the viewport — then
page edge, canvas and theme-color are all --paper by construction, whatever the
animation is doing, and the seam cannot come back.
Costs nothing to look at: this falls inside the strip Safari's toolbar already
covers. Static, so the layer still composites without repainting per frame. */
.ambient::after {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 5rem;
background: linear-gradient(to top, var(--paper), rgb(10 16 12 / 0%));
}
/* Oversized so the bands can travel a long way without an edge ever entering the /* Oversized so the bands can travel a long way without an edge ever entering the
frame — what should be visible is the light moving, never the thing carrying it. */ frame — what should be visible is the light moving, never the thing carrying it. */
.ambient-haze, .ambient-haze,
@@ -241,6 +273,7 @@ const canonical = new URL('/', Astro.site);
/* One inset for everything. It is deliberately generous: at mid widths a tighter /* One inset for everything. It is deliberately generous: at mid widths a tighter
margin puts the copy and the window edge in each other's way. */ margin puts the copy and the window edge in each other's way. */
--edge: clamp(1.35rem, 4.8vw, 5rem); --edge: clamp(1.35rem, 4.8vw, 5rem);
--shell-y: clamp(1.25rem, 2.6vh, 2.5rem);
position: relative; position: relative;
display: grid; display: grid;
@@ -248,7 +281,7 @@ const canonical = new URL('/', Astro.site);
width: min(100%, 1600px); width: min(100%, 1600px);
min-height: 100svh; min-height: 100svh;
margin: 0 auto; margin: 0 auto;
padding: clamp(1.25rem, 2.6vh, 2.5rem) var(--edge); padding: var(--shell-y) var(--edge);
} }
.masthead, .masthead,