From 058fecd27fdfbc79548ea0d35cddb66a5704670c Mon Sep 17 00:00:00 2001 From: TMC Date: Thu, 12 Nov 2026 22:11:53 +0100 Subject: [PATCH] Website: the story phone comes apart as its eighth chapter The separate Under the glass section is gone; its exploded phone is now chapter 8 of How it works, so the pinned story phone carries straight into it. After Out of sight the list closes, the drawn phone is swapped for its layered double (the same home screen closed; measured at about 1-2% of pixels apart, from anti-aliasing), which tilts and comes apart, holds, and goes back together before the section ends. The chapter is two screens long and its text stays put meanwhile. Labels redone: a column of numbered solid tags beside the stack, evenly spaced, each joined by an elbow line to a dot on its layer's edge; the stack moves left as it opens to make room. The glass sheen and the frame now match the drawn phone, so the swap is not seen. Also fixed: the layered phone sat at its full 414 px until first used, which made the page wider than a phone and zoomed it out; it is scaled from the start, and on phones the story stage clips. The story now counts its progress in chapter heights rather than window heights, which drifted apart on phones when the address bar hides. The long chapter's height lost to the general chapter rule; it now wins. Section numbers close up (Details 02 to Get it 06); the page alphabet's U leads to the new chapter. Co-Authored-By: Claude Opus 5.5 --- WEBSITE/README.md | 6 +- WEBSITE/index.html | 68 +++++++++--------- WEBSITE/script.js | 171 ++++++++++++++++++++++++++++++++++----------- WEBSITE/styles.css | 70 ++++++++++--------- 4 files changed, 205 insertions(+), 110 deletions(-) diff --git a/WEBSITE/README.md b/WEBSITE/README.md index 4075822ff7..9c341ce763 100644 --- a/WEBSITE/README.md +++ b/WEBSITE/README.md @@ -35,7 +35,7 @@ and open . ## The phone The phone is drawn in HTML at 390 x 844 and scaled to fit, four times over: a looping teaser in -the hero, one driven by the scroll position through the seven "How it works" chapters +the hero, one driven by the scroll position through the eight "How it works" chapters (`storyState()` in `script.js`), one in the playground that answers to touch and mouse, and the list phone of the race (the playground's controller, locked to the app list). All of them render the same state object, described in `blank()`. The 40 app names and icons on it are generic @@ -49,7 +49,9 @@ Other moving parts, all in `script.js`: wave, drag to jump between sections, tap to scroll there. On phones it shows while scrolling. - **Detail-card switches** (`setupLook`): left-handed alphabet, comfy rows and icon shape, applied to every phone on the page. -- **Under the glass** (`setupAnatomy`): the phone pulled apart into its layers as the section scrolls. +- **Every layer, pulled apart** (`setupAnatomy`): the story's eighth chapter. The story phone is + swapped for a double built from separate layers (it looks the same closed), which then comes + apart in 3D with a labelled tag per layer and goes back together. - **Race** (`setupRace`): find an app in a shuffled grid of 40, then in the list with the alphabet. - **Magnetic buttons and the finger cursor** (`setupPointerFx`), desktop only. - **Easter eggs**: scrub to F in the playground, or type "flexera" anywhere. diff --git a/WEBSITE/index.html b/WEBSITE/index.html index 789b24e2c4..d2b0b8adf5 100644 --- a/WEBSITE/index.html +++ b/WEBSITE/index.html @@ -169,16 +169,27 @@
-

01 / 07

+

01 / 08

Home, minus the noise.

Pages, folders and widgets, in the Android layout you already know. No feed, no ads, nothing you didn't put there yourself.

Gesture None. Just look.

@@ -186,7 +197,7 @@
-

02 / 07

+

02 / 08

Swipe up. A to Z.

Every app in one clean column, straight over your wallpaper. New messages show up under the app's name, so you see them before you open anything.

Gesture Swipe up

@@ -194,7 +205,7 @@
-

03 / 07

+

03 / 08

Ride the wave.

Slide your thumb along the letters on the edge. They swell around it like a wave, and the list drops that letter into the middle of the screen, right where your thumb already is. Swipe up and keep holding to scrub straight from home.

Gesture Slide on the edge

@@ -202,7 +213,7 @@
-

04 / 07

+

04 / 08

Folders in focus.

Open a folder and the rest of the screen melts into a soft blur, wallpaper included. Just the apps you came for.

Gesture Tap

@@ -210,7 +221,7 @@
-

05 / 07

+

05 / 08

Swipe into nothing.

There's no page left of your first one. Swipe there anyway and your camera opens. Or any app, or any shortcut you pick. Works past your last page too.

Gesture Swipe past the edge

@@ -218,7 +229,7 @@
-

06 / 07

+

06 / 08

Widgets that fit in.

A clock that lines up with your icons: the date, a one-line forecast, left, center or right, thin to bold. Next to it, a picture frame in your icon shape that turns to the next photo every ten seconds.

Gesture Hold, then edit

@@ -226,41 +237,28 @@
-

07 / 07

+

07 / 08

Out of sight.

Hold any app in the list and tap Hide app. Gone from the list and from search. Changed your mind? Undo, or bring it back from Settings whenever you like.

Gesture Hold, then hide

+
+
+

08 / 08

+

Every layer. Pulled apart.

+

Under the glass sit five layers: your wallpaper, the widgets, icons and folders, the app list with its wave alphabet, and the glass on top. No feed and no ads hiding in between.

+

Gesture Keep scrolling

+
+
- -
-
-
-

[ 02 ] Under the glass

-

- Every layer. - Pulled apart. -

-
- -
    -
  1. GlassNo feed. No ads.
  2. -
  3. App listA to Z, wave alphabet on the edge.
  4. -
  5. Icons & foldersBlur behind every open folder.
  6. -
  7. WidgetsClock, weather, pictures.
  8. -
  9. WallpaperYours. Untouched.
  10. -
-
-
-
-

[ 03 ] The details

+

[ 02 ] The details

Small things. Done properly. @@ -327,7 +325,7 @@
-

[ 04 ] Privacy

+

[ 03 ] Privacy

It goes online for exactly two things.

@@ -348,7 +346,7 @@
-

[ 05 ] Playground

+

[ 04 ] Playground

Go on. Touch it. @@ -385,7 +383,7 @@
-

[ 06 ] Race

+

[ 05 ] Race

Find it. Faster. @@ -415,7 +413,7 @@
-

[ 07 ] Get it

+

[ 06 ] Get it

Get Flexera. diff --git a/WEBSITE/script.js b/WEBSITE/script.js index 2f74fe3f38..2ece5e06bb 100644 --- a/WEBSITE/script.js +++ b/WEBSITE/script.js @@ -123,6 +123,8 @@ const MENU_X = 120; const CLOCK_W = 4 * CW - 36; const DEFAULT_WEIGHT = 300; + /** The story runs from 0 to 9: chapters 1 to 7 take one unit each, chapter 8 two. */ + const STORY_END = 9; /** Icon shapes, as border radii for icons and for the picture widget. */ const SHAPES = { squircle: { icon: '30%', widget: '30px' }, @@ -525,7 +527,17 @@ */ function storyState(ph, P) { const s = blank(); - P = clamp(P, 0, 7); + P = clamp(P, 0, STORY_END); + if (P >= 7) { + // Chapter 8, two units long: the list closes and the phone is home again. From there on the + // layered phone (setupAnatomy) takes its place and comes apart; this one is hidden. + const t = (P - 7) / 2; + s.drawer = 1 - E(t, 0, 0.1); + s.list = ph.scrollFor(L('P')); + s.hidden = { Podcasts: 1 }; + s.snack = 1 - E(t, 0, 0.08); + return s; + } const ch = Math.min(6, Math.floor(P)); const t = P - ch; const F = s.finger; @@ -636,7 +648,11 @@ return s; } - function setupStory(ph) { + /** Chapter 8, from its start (0) to its end (1): when the phones swap, and how far apart the layers are. */ + const SWAP_AT = 0.12; + const layersApart = (t) => smooth(seg(t, 0.14, 0.42)) * (1 - smooth(seg(t, 0.74, 0.94))); + + function setupStory(ph, layered) { const box = document.querySelector('.story__steps'); const steps = [...document.querySelectorAll('[data-step]')]; const num = document.querySelector('[data-story-num]'); @@ -644,14 +660,22 @@ let lastChapter = -1; let lastP = -1; return function update(force) { + // In chapters, not window heights: on phones a chapter is the small viewport height, while + // the window grows when the browser's address bar slides away. const vh = window.innerHeight; - const P = clamp(-box.getBoundingClientRect().top / vh + 0.5, 0, 7); - const ch = Math.min(6, Math.floor(P)); - // Without motion, each chapter shows its finished picture. - const shown = reduced ? ch + 0.985 : P; + const unit = steps[0].offsetHeight || vh; + const P = clamp((-box.getBoundingClientRect().top + vh / 2) / unit, 0, STORY_END); + const ch = P >= 7 ? 7 : Math.floor(P); + // Without motion, each chapter shows its finished picture (chapter 8: pulled apart). + const shown = reduced ? (ch === 7 ? 7.98 : ch + 0.985) : P; if (force || Math.abs(shown - lastP) > 0.0002) { lastP = shown; ph.render(storyState(ph, shown)); + if (layered) { + const t = shown >= 7 ? (shown - 7) / 2 : 0; + const swapped = shown >= 7 && t >= SWAP_AT; + layered.set(swapped ? layersApart(t) : 0, swapped, force); + } } if (ch !== lastChapter) { lastChapter = ch; @@ -1294,6 +1318,10 @@ el.style.setProperty('--shape', shape.icon); el.style.setProperty('--shape-w', shape.widget); }); + document.querySelectorAll('.xphone').forEach((el) => { + el.classList.toggle('is-lefty', look.lefty); + el.classList.toggle('is-comfy', look.comfy); + }); rerender(); sync(); toast(message); @@ -1556,35 +1584,48 @@

`; } - function setupAnatomy() { - const sec = document.querySelector('.anatomy'); + /** + * The story phone's double, built from separate layers so that it can come apart in 3D (the + * drawn phone clips its contents and cannot). It sits exactly over the story phone; closed, it + * shows the same home screen, so swapping the two is not seen. + */ + function setupAnatomy(storyPhone) { const host = document.querySelector('[data-xphone]'); - if (!sec || !host) return null; + if (!host || !storyPhone) return null; host.innerHTML = xphoneMarkup(); const xp = host.querySelector('.xphone'); const layers = [...xp.querySelectorAll('.xl')]; // Labels from the top: glass, list, icons, widgets, wallpaper. const pins = [5, 4, 3, 2, 1].map((i) => layers[i].querySelector('.xl__pin')); const labels = [...document.querySelectorAll('[data-xlabels] li')]; - const sticky = sec.querySelector('.anatomy__sticky'); + const stage = host.closest('.story__stage'); + const rail = stage.querySelector('[data-story-rail]'); + const pics = [...xp.querySelectorAll('.pic')]; + const svg = stage.querySelector('[data-xlines]'); + const NS = 'http://www.w3.org/2000/svg'; + const lines = labels.map(() => { + const path = document.createElementNS(NS, 'path'); + const dot = document.createElementNS(NS, 'circle'); + dot.setAttribute('r', '4.5'); + svg.append(path, dot); + return { path, dot }; + }); let scale = 1; - const fit = () => { scale = host.clientWidth / 414; }; - fit(); - if ('ResizeObserver' in window) new ResizeObserver(fit).observe(host); let lastE = -1; - return function update(force) { - const rc = sec.getBoundingClientRect(); - const vh = window.innerHeight; - if (!force && (rc.bottom < -50 || rc.top > vh + 50)) return; - const p = clamp(-rc.top / Math.max(1, rc.height - vh)); - let e = smooth(seg(p, 0.08, 0.4)) * (1 - smooth(seg(p, 0.8, 0.98))); - if (reduced) e = p > 0.04 && p < 0.96 ? 1 : 0; - if (!force && Math.abs(e - lastE) < 0.0005) return; - lastE = e; + let swapped = null; + /** Same picture as the story phone shows, so the swap is not seen. */ + function syncPictures() { + pics.forEach((p, i) => p.classList.toggle('on', i === storyPhone.picIndex)); + } + /** Puts the layers where e (0 closed, 1 fully apart) wants them. */ + function arrange(e) { const GAP = 150; // between layers, in the phone's pixels const tilt = 56 * e; - const sc = scale * (1 - 0.28 * e); - const ox = 207 * (scale - sc); + const sc = scale * (1 - 0.36 * e); + const wide = window.innerWidth > 900; + // Opening, the stack moves left to leave room for its labels. + const shift = wide ? -e * Math.min(140, stage.clientWidth * 0.17) : 0; + const ox = 207 * (scale - sc) + shift; // The layers rise as they separate; lower the whole stack by half of that, to keep it centred. const oy = 434 * (scale - sc) + e * 2.5 * GAP * Math.sin((tilt * Math.PI) / 180) * sc; xp.style.setProperty('--e', e.toFixed(3)); @@ -1594,19 +1635,66 @@ }); // Flat, it is just a home screen: the app list layer only shows once the stack opens. layers[4].style.opacity = smooth(seg(e, 0.15, 0.6)).toFixed(3); - const wide = window.innerWidth > 900; - const base = sticky.getBoundingClientRect(); - let prevY = -Infinity; - labels.forEach((li, k) => { - li.style.opacity = seg(e, 0.55, 0.95).toFixed(3); - if (!wide) return; - const pr = pins[k].getBoundingClientRect(); - const x = Math.min(pr.left - base.left + 16, base.width - 250); - const y = Math.max(pr.top - base.top - 12, prevY + 60); // never on top of the one above - prevY = y; - li.style.transform = `translate(${x.toFixed(1)}px, ${y.toFixed(1)}px)`; + } + /** The labels: one column beside the stack, evenly spaced, each joined to its layer's edge. */ + function label(e) { + const shown = seg(e, 0.55, 0.95); + svg.style.opacity = shown.toFixed(3); + labels.forEach((li) => { li.style.opacity = shown.toFixed(3); }); + if (window.innerWidth <= 900 || shown <= 0) return; + const base = stage.getBoundingClientRect(); + let right = -Infinity; + for (const l of layers) right = Math.max(right, l.getBoundingClientRect().right); + const LABEL_W = 214; + const colX = clamp(right - base.left + 34, 0, base.width - LABEL_W - 8); + const pts = pins.map((pin) => { + const r = pin.getBoundingClientRect(); + return { x: r.left - base.left, y: r.top - base.top }; }); + const ys = []; + let prev = -Infinity; + for (const pt of pts) { + const y = Math.max(pt.y, prev + 76); // never closer than one tag's height + ys.push(y); + prev = y; + } + const spill = ys[ys.length - 1] + 44 - base.height; + if (spill > 0) for (let k = 0; k < ys.length; k++) ys[k] -= spill; + labels.forEach((li, k) => { + li.style.transform = `translate(${colX.toFixed(1)}px, ${ys[k].toFixed(1)}px)`; + const { x, y } = pts[k]; + const bend = Math.max(x + 16, colX - 26); + lines[k].path.setAttribute('d', `M${x.toFixed(1)} ${y.toFixed(1)} L${bend.toFixed(1)} ${ys[k].toFixed(1)} L${colX.toFixed(1)} ${ys[k].toFixed(1)}`); + lines[k].dot.setAttribute('cx', x.toFixed(1)); + lines[k].dot.setAttribute('cy', y.toFixed(1)); + }); + } + const fit = () => { + scale = host.clientWidth / 414; + arrange(Math.max(0, lastE)); // scaled from the start: at full size it would widen the page }; + fit(); + if ('ResizeObserver' in window) new ResizeObserver(fit).observe(host); + function set(e, on, force) { + if (on !== swapped) { + swapped = on; + host.style.visibility = on ? 'visible' : 'hidden'; + storyPhone.host.style.visibility = on ? 'hidden' : ''; + if (on) syncPictures(); + lastE = -1; + } + if (!on) { + label(0); + rail.style.opacity = ''; + return; + } + if (!force && Math.abs(e - lastE) < 0.0005) return; + lastE = e; + arrange(e); + rail.style.opacity = (1 - e).toFixed(3); + label(e); + } + return { set, syncPictures, element: xp }; } /* ------------------------------------------------------------------ the race */ @@ -1816,11 +1904,11 @@ document.querySelectorAll('.xl [data-date]').forEach((el) => { el.textContent = date; }); } - const story = phones.story ? setupStory(phones.story) : null; + const layered = setupAnatomy(phones.story); + const story = phones.story ? setupStory(phones.story, layered) : null; const play = phones.play ? setupPlay(phones.play, { caption: document.querySelector('[data-caption]'), demos: document.querySelector('.play'), egg: true }) : null; - const anatomy = setupAnatomy(); const race = phones.race ? setupRace(phones.race) : null; tick(); setInterval(tick, 10000); @@ -1829,7 +1917,6 @@ if (story) story(true); if (play) play.render(); if (race) race.render(); - if (anatomy) anatomy(true); }; setupLook(phones, rerender); const pageWave = setupPageWave(); @@ -1838,7 +1925,12 @@ setupEggs(); // Picture widgets turn to the next picture now and then. - if (!reduced) setInterval(() => Object.values(phones).forEach((p) => p.nextPicture()), 3800); + if (!reduced) { + setInterval(() => { + Object.values(phones).forEach((p) => p.nextPicture()); + if (layered) layered.syncPictures(); + }, 3800); + } // Fonts change nothing that is measured, but measure again once they are in, to be sure. if (document.fonts && document.fonts.ready) { @@ -1882,7 +1974,6 @@ } } if (story) story(); - if (anatomy) anatomy(); pageWave(); } diff --git a/WEBSITE/styles.css b/WEBSITE/styles.css index f3405e871f..38859d85ab 100644 --- a/WEBSITE/styles.css +++ b/WEBSITE/styles.css @@ -289,7 +289,12 @@ section { position: relative; } color: rgba(245, 244, 255, .045); /* filled, not outlined, for the same reason as .hero__ghost */ pointer-events: none; user-select: none; } -.story .phone-host { height: min(84vh, 880px); height: min(84svh, 880px); position: relative; z-index: 1; } +.story__phones { position: relative; z-index: 1; height: min(84vh, 880px); height: min(84svh, 880px); aspect-ratio: 414 / 868; } +.story__phones .phone-host, .story__phones .xhost { position: absolute; inset: 0; height: auto; } +.story__phones .xhost { visibility: hidden; } +/* Chapter 8 is two screens long; its text stays put while the phone comes apart. */ +.step.step--long { min-height: 200vh; min-height: 200svh; align-items: flex-start; } +.step.step--long .step__card { position: sticky; top: max(96px, calc(50vh - 170px)); } .story__rail { position: absolute; right: 0; top: 50%; translate: 0 -50%; display: flex; flex-direction: column; gap: 10px; margin: 0; padding: 0; list-style: none; } .story__rail li { width: 12px; height: 34px; border: var(--b) solid var(--fg); transition: background .3s, height .3s; } .story__rail li.done { background: var(--fg); } @@ -655,12 +660,12 @@ section { position: relative; } .row__p { margin-top: 3px; font: 400 13px/1.2 var(--ui); color: #cfcbef; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .row.flash { background: rgba(255, 255, 255, .12); } /* Settings from the detail cards. */ -.screen.is-comfy .row { height: 66px; } -.screen.is-comfy .row--note { height: 78px; } -.screen.is-comfy .row .ic { width: 48px; height: 48px; } -.screen.is-comfy .row__n { font-size: 19.5px; } -.screen.is-lefty .list { padding: 0 20px 0 58px; } -.screen.is-lefty .alpha { right: auto; left: 8px; } +.is-comfy .row { height: 66px; } +.is-comfy .row--note { height: 78px; } +.is-comfy .row .ic { width: 48px; height: 48px; } +.is-comfy .row__n { font-size: 19.5px; } +.is-lefty .list { padding: 0 20px 0 58px; } +.is-lefty .alpha { right: auto; left: 8px; } .row.held { background: rgba(255, 255, 255, .1); } .alpha { position: absolute; right: 8px; bottom: 112px; width: 30px; display: flex; flex-direction: column; align-items: center; z-index: 2; } @@ -767,30 +772,34 @@ section { position: relative; } .over-phone .phone-host, .over-phone .phone-host * { cursor: none !important; } .btn, .chip { transition: transform .14s ease, box-shadow .14s ease, background .2s, color .2s, translate .35s cubic-bezier(.2, .8, .2, 1); } -/* ---------- under the glass: the phone pulled apart ---------- */ -.anatomy { position: relative; height: 320vh; height: 320svh; } -.anatomy__sticky { - position: sticky; top: 0; height: 100vh; height: 100svh; overflow: hidden; - display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); align-items: center; - max-width: var(--max); margin: 0 auto; padding: 70px var(--pad) 30px; -} -.anatomy__stage { position: relative; display: grid; place-items: center; height: 100%; } -.xhost { position: relative; height: min(68vh, 720px); height: min(68svh, 720px); aspect-ratio: 414 / 868; perspective: 2600px; } +/* ---------- chapter 8: the phone pulled apart ---------- */ +.xhost { perspective: 2600px; } .xphone { position: absolute; left: 0; top: 0; width: 414px; height: 868px; transform-origin: 0 0; transform-style: preserve-3d; --e: 0; } .xl { position: absolute; left: 12px; top: 12px; width: 390px; height: 844px; border-radius: 48px; overflow: hidden; font-family: var(--ui); color: #fff; box-shadow: 0 0 0 2px rgba(118, 250, 252, calc(var(--e) * .55)); } .xl * { box-sizing: border-box; } .xl .ic svg, .xl .gl { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } -.xl--body { left: 0; top: 0; width: 414px; height: 868px; border-radius: 60px; background: linear-gradient(150deg, #34324d, #121122 45%, #08070f); box-shadow: inset 0 0 0 2px #4a4767, 0 50px 90px -30px rgba(101, 61, 240, .7); } +.xl--body::before, .xl--body::after { content: ""; position: absolute; right: -4px; width: 4px; border-radius: 0 3px 3px 0; background: #2b2940; } +.xl--body::before { top: 190px; height: 70px; } +.xl--body::after { top: 290px; height: 110px; } +.xl--body { overflow: visible; left: 0; top: 0; width: 414px; height: 868px; border-radius: 60px; background: linear-gradient(150deg, #34324d, #121122 45%, #08070f); box-shadow: inset 0 0 0 2px #4a4767, inset 0 0 0 5px #0a0914, 0 50px 90px -30px rgba(101, 61, 240, .7), 0 0 0 2px #000; } .xl--list { background: rgba(5, 4, 20, calc(.35 + var(--e) * .3)); } .xl--list .list { top: 46px; } .xl--list .alpha { bottom: 112px; } -.xl--glass { background: linear-gradient(125deg, rgba(255, 255, 255, .2), rgba(255, 255, 255, 0) 38%, rgba(255, 255, 255, .07) 68%, rgba(255, 255, 255, 0)); } -.xl__pin { position: absolute; right: 0; top: 46%; width: 1px; height: 1px; } +.xl--glass::before { content: ""; position: absolute; inset: 0; opacity: var(--e); background: linear-gradient(125deg, rgba(255, 255, 255, .2), rgba(255, 255, 255, 0) 38%, rgba(255, 255, 255, .07) 68%, rgba(255, 255, 255, 0)); } +.xl__pin { position: absolute; right: 0; top: 30%; width: 1px; height: 1px; } +.anatomy__lines { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; z-index: 2; opacity: 0; } +.anatomy__lines path { fill: none; stroke: var(--cyan); stroke-width: 2; } +.anatomy__lines circle { fill: var(--bg); stroke: var(--cyan); stroke-width: 2.5; } .anatomy__labels { list-style: none; margin: 0; padding: 0; } -.anatomy__labels li { position: absolute; left: 0; top: 0; width: 240px; padding-left: 66px; opacity: 0; pointer-events: none; will-change: transform; } -.anatomy__labels li::before { content: ""; position: absolute; left: 0; top: .55em; width: 56px; height: 2px; background: var(--cyan); } -.anatomy__labels b { display: block; font: 900 17px/1.1 var(--display); text-transform: uppercase; letter-spacing: -.02em; } -.anatomy__labels span { display: block; margin-top: 5px; font: 500 14px/1.35 var(--body); color: var(--dim); } +.anatomy__labels li { + position: absolute; left: 0; top: 0; z-index: 3; width: 214px; translate: 0 -50%; + padding: 9px 12px 10px; background: rgba(7, 7, 13, .94); + border: var(--b) solid var(--fg); box-shadow: 4px 4px 0 var(--violet); + opacity: 0; pointer-events: none; will-change: transform; +} +.anatomy__labels i { margin-right: 8px; font: 700 11px/1 var(--mono); font-style: normal; color: var(--cyan); } +.anatomy__labels b { font: 900 13px/1.15 var(--display); text-transform: uppercase; letter-spacing: -.01em; } +.anatomy__labels span { display: block; margin-top: 6px; font: 500 12px/1.3 var(--mono); color: var(--dim); white-space: nowrap; } /* ---------- race ---------- */ .race__grid { @@ -856,8 +865,10 @@ section { position: relative; } .spinner { width: 100px; height: 100px; left: 2%; } .story__body { display: block; padding: 0; } - .story__stage { position: sticky; top: 0; place-items: start center; padding-top: 70px; } - .story .phone-host { height: 52vh; height: 52svh; } + .story__stage { position: sticky; top: 0; place-items: start center; padding-top: 70px; overflow: hidden; } /* nothing the phones do may widen the page */ + .story__phones { height: 52vh; height: 52svh; } + .step.step--long { align-items: flex-end; } + .step.step--long .step__card { position: sticky; top: auto; bottom: 4svh; } .story__num { top: 32%; font-size: 58vw; } .story__rail { right: 14px; top: 32%; gap: 6px; } .story__rail li { width: 8px; height: 20px; } @@ -892,14 +903,7 @@ section { position: relative; } .pagewave a { height: 25px; } .pagewave__bubble { right: 58px; } - .anatomy { height: 260svh; } - .anatomy__sticky { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto; padding-top: 74px; gap: 8px; } - .anatomy__head .h2 { font-size: clamp(32px, 9vw, 48px); } - .xhost { height: 44svh; } - .anatomy__labels { display: flex; flex-wrap: wrap; gap: 6px; padding-bottom: 6px; } - .anatomy__labels li { position: static; width: auto; padding: 7px 10px; border: var(--b) solid var(--fg); transform: none !important; font-size: 12px; } - .anatomy__labels li::before, .anatomy__labels span { display: none; } - .anatomy__labels b { font-size: 11px; } + .anatomy__labels, .anatomy__lines { display: none; } /* the chapter's text names the layers */ .race__grid { grid-template-columns: minmax(0, 1fr); } .race__side { order: -1; }