类型:Site模板 · 收录:2026-11-05 01:12:49 · 源码:会员可见
# Recreate this site as a single HTML file: HELD-MINT — Rare cards behind steel
You are an expert creative front-end developer. Produce a **single self-contained `index.html`**
that reproduces the project below **exactly**: same layout, copy, visuals, motion and interaction.
Use pure HTML/CSS/JS in one file, with no build step, framework or bundler. ES modules go inline in a
`<script type="module">`. **Lenis 1.3.26 is the only external code**, pulled through an import map; the
four type faces come from one Google Fonts `<link>`. You write everything else by hand: the spring
engine, the shared ticker, the reveal triggers, the scroll parallax, the line-by-line and
letter-by-letter text reveals, the spray-paint underline, the poster menu and the "pile + stamp"
preloader. Hardcode every value given here as a fixed constant. Where this document gives code, it is
the shipped behaviour: use it as it stands, and treat every number in it as the shipped default.
```js
const ASSET_BASE_URL = "https://storage.getlayers.ai/assets/held-mint-78f917a508";
```
Every image below loads from that base. A path written like `hero/hero-vault.png` in this document
means `${ASSET_BASE_URL}/hero/hero-vault.png` (the bucket sends `Access-Control-Allow-Origin: *`).
The full list is in **Assets** at the end.
## What it is
The one-page landing of **HELD-MINT**, a Swiss card vault: rare collectible cards (cartoon "Ape" cards,
NFT-style art printed on physical cards) bought outright, graded, sealed and shelved in a strongroom in
Zug, with ownership, votes and audits kept on a public ledger. The look is **street poster on black
steel**: a near-black blue ink ground (`#070a10`), paper-white Barlow Condensed **Black Italic**
display type in huge uppercase, one electric blue accent (`#4a8cff`) used as hand-sprayed graffiti — a
Sedgwick Ave Display script word tilted over every headline with a sprayed scribble under it, crowns,
paint drips running off every button — and real card art scattered across the page at angles, as if
slapped down on a table. Barbed-wire strands cross the page between sections. A screen-blended film
grain sits over everything.
Order, top to bottom:
0. **Preloader — "pile + stamp".** A black curtain. Four of the site's cards slap down onto the table
one by one as the page loads while a big `000 → 100` counter and a blue bar fill along the bottom;
at 100 a blue **SEALED** rubber stamp hits the pile and the whole curtain lifts off upward,
uncovering the hero, whose reveals start as it lifts.
1. **Header** (fixed): HELD-MINT logo, locale, four nav links with a sprayed underline on hover,
Log In, a "Buy NFT" paint plate; a burger on tablets/phones opens a tilted poster menu.
2. **Hero** (full viewport): the vault-door photo easing back as its lights come on, "RARE CARDS /
BEHIND *steel*", a lead and a "Request access" plate; a tilted card peeking out of the bottom.
3. **Cards + statement**: two big tilted cards and a long display statement ending in blue "and
nothing else.", an aside and four tag chips.
4. Barbed wire.
5. **Process**: "FROM THE TABLE / TO THE ~~SHOEBOX~~ *VAULT*" — four tilted step cards.
6. **Vault**: "CURRENTLY *held*" — a five-card fan opening with the scroll, a stats plate, a CTA.
7. **Featured drop**: a light paper poster "DROP I *KR777*" with a big card, wrapped in barbed-wire arcs.
8. **Record**: "EVERYTHING IS / ON THE *record*" — four stat stickers and an audit ledger with stamps
pressed onto each row.
9. **Questions**: "ASK US / THE HARD *stuff*" — a native one-open-at-a-time accordion.
10. Barbed wire.
11. **Join**: "PUT YOUR CARDS / BEHIND THE *door*" — email field + "Enter the vault".
12. **Footer**: address, three link columns, legal, and a giant outlined HELD-MINT wordmark whose
letters stand up one by one.
**Springs are the motion model.** Every entrance, hover and parallax is a spring (react-spring
semantics, exact tension/friction below); the only CSS transitions are the 250 ms colour swaps on hover.
Smooth scroll is Lenis.
## Page shell & libraries
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#070a10">
<title>HELD-MINT — Rare cards behind steel</title>
<meta name="description" content="Rare trading cards bought outright, graded, sealed and stored in a Swiss strongroom in Zug — every shelf, seal and signature published on a public ledger.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:ital,wght@0,600;1,900&family=Sedgwick+Ave+Display&family=Sora:wght@400&display=swap" rel="stylesheet">
<script type="importmap">
{ "imports": { "lenis": "https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.mjs" } }
</script>
```
**Faces** — exactly four, and only these are drawn:
- **Sora 400** — body copy (the page default, `font-family: var(--font-sans)`).
- **Barlow Condensed 600 upright** — every caps label, nav, chip, stat label, ledger row (`type-caps`).
- **Barlow Condensed 900 italic** — every display headline, button label, logo, number (`type-display`).
- **Sedgwick Ave Display 400** — the hand-written graffiti words, eyebrows and ghost words (`type-script`).
```css
:root { --font-sans: "Sora", sans-serif; --font-display: "Barlow Condensed", sans-serif; --font-script: "Sedgwick Ave Display", cursive; }
.type-display { font-family: var(--font-display); font-weight: 900; font-style: italic; text-transform: uppercase; white-space: nowrap; line-height: 1; }
.type-caps { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; white-space: nowrap; line-height: 1; }
.type-script { font-family: var(--font-script); font-weight: 400; white-space: nowrap; line-height: 1; }
```
**Reset** (Tailwind v4 preflight equivalent): `*, ::before, ::after { box-sizing: border-box; margin: 0;
padding: 0; border: 0 solid; }`, `html { line-height: 1.5; -webkit-text-size-adjust: 100% }`,
`h1,h2,h3,p { font-size: inherit; font-weight: inherit }`, `ol, ul { list-style: none }`,
`a { color: inherit; text-decoration: inherit }`, `img, svg, picture { display: block; max-width: 100% }`
(an `<img>` inside a `<picture>` is still `display:block`), buttons and inputs unstyled
(`background: none; font: inherit; color: inherit`), `button, summary { cursor: pointer }`, and `.sr-only`
(the standard visually-hidden clip). `body { background: var(--background); color: var(--foreground);
font-family: var(--font-sans) }`.
**Page structure.** `<body>` holds one `<main style="position:relative; overflow-x:clip;
background:var(--background)">` with, in order: header · hero · intro · barbed wire 1 · process · vault ·
drop · record · questions · barbed wire 2 · the Join + Footer wrapper · the grain overlay · the preloader
overlay. The poster menu overlay is appended to `<body>` (outside `<main>`).
**Grain overlay** (Figma "Texture — grain (screen)", over the whole page, header included):
`<div aria-hidden style="pointer-events:none; position:absolute; inset:0; z-index:30;
background-image:url(${ASSET_BASE_URL}/shared/texture-grain.webp); background-size:32rem;
opacity:.2; mix-blend-mode:screen">` as the last child of `<main>` before the preloader. (`32rem` = 512
design px; it tiles and scales with the frame.)
**Smooth scroll**: on load call `window.scrollTo(0, 0)`, then `const lenis = new Lenis({ smoothWheel: true })`
driven by its own `requestAnimationFrame(t => { lenis.raf(t); … })` loop. **Stopping scroll** (while the
preloader is up and while the menu is open) means `lenis.stop()` plus
`html.style.position = "relative"; html.style.overflow = "hidden"; html.style.height = "100%"`.
**Starting it** means `lenis.start()` and removing those three properties. Hash links (`#join`,
`#process`, `#vault`) are plain anchors.
## Tokens (copy verbatim into `:root`)
The design is dark-only. Tier 1 holds literals, Tier 2 names roles; every role is re-pointed per
breakpoint (so `--type-display` is 150 px on desktop and 48 px on mobile). Copy both tiers and the three
media blocks as they stand.
```css
:root {
--raw-color-ink-1000: #05070b;
--raw-color-ink-975: #07090d;
--raw-color-ink-950: #070a10;
--raw-color-ink-900: #080c11;
--raw-color-ink-850: #0c1119;
--raw-color-paper-50: #f2f4f8;
--raw-color-paper-200: #e6e7ea;
--raw-color-slate-200: #d5dae3;
--raw-color-slate-250: #c9d0db;
--raw-color-slate-400: #8f98a8;
--raw-color-blue-500: #4a8cff;
--raw-color-blue-600: #2a63f0;
--raw-color-white: #ffffff;
--raw-gradient-header-scrim: radial-gradient(110.46% 100% at 50% 0%, rgb(8 12 17 / 1) 0%, rgb(8 12 17 / 0) 100%);
--raw-gradient-header-shade: linear-gradient(180deg, rgb(7 10 16 / 0.92) 0%, rgb(7 10 16 / 0.55) 50%, rgb(7 10 16 / 0) 100%);
--raw-gradient-hero-vignette: radial-gradient(52.63% 66.67% at 47.37% 50%, rgb(7 10 16 / 0) 0%, rgb(7 10 16 / 0) 55%, rgb(7 10 16 / 0.72) 100%);
--raw-gradient-hero-fade: linear-gradient(180deg,
rgb(7 10 16 / 0) 0%, rgb(7 10 16 / 0.06) 8.8235%, rgb(7 10 16 / 0.24) 17.647%, rgb(7 10 16 / 0.52) 26.471%,
rgb(7 10 16 / 0.76) 33.824%, rgb(7 10 16 / 0.92) 41.176%, rgb(7 10 16 / 1) 48.529%, rgb(7 10 16 / 1) 65.441%,
rgb(7 10 16 / 0.88) 73.529%, rgb(7 10 16 / 0.55) 82.353%, rgb(7 10 16 / 0.2) 91.176%, rgb(7 10 16 / 0) 100%);
--raw-gradient-hero-fade-compact: linear-gradient(180deg, rgb(7 10 16 / 0) 0%, rgb(7 10 16 / 0.88) 60%, rgb(7 10 16 / 1) 100%);
--raw-gradient-card-fade: linear-gradient(180deg, rgb(7 10 16 / 0.96) 0%, rgb(7 10 16 / 0.8) 25%, rgb(7 10 16 / 0.35) 60%, rgb(7 10 16 / 0) 100%);
--raw-shadow-card: 0.5rem 0.875rem 1.5rem 0 rgb(0 0 0 / 0.35);
--raw-drop-shadow-wire: 0.5rem 0.75rem 0.625rem rgb(0 0 0 / 0.4);
--raw-drop-shadow-wire-compact: 0.375rem 0.625rem 0.625rem rgb(0 0 0 / 0.4);
/* Type scale — Figma px ÷ 16 */
--raw-font-size-380: 23.75rem; --raw-font-size-360: 22.5rem; --raw-font-size-337: 21.0625rem;
--raw-font-size-330: 20.625rem; --raw-font-size-300: 18.75rem; --raw-font-size-280: 17.5rem;
--raw-font-size-260: 16.25rem; --raw-font-size-248: 15.5rem; --raw-font-size-240: 15rem;
--raw-font-size-220: 13.75rem; --raw-font-size-200: 12.5rem; --raw-font-size-190: 11.875rem;
--raw-font-size-186: 11.625rem; --raw-font-size-170: 10.625rem; --raw-font-size-153: 9.5625rem;
--raw-font-size-150: 9.375rem; --raw-font-size-140: 8.75rem; --raw-font-size-132: 8.25rem;
--raw-font-size-130: 8.125rem; --raw-font-size-128: 8rem; --raw-font-size-122: 7.625rem;
--raw-font-size-120: 7.5rem; --raw-font-size-106: 6.625rem; --raw-font-size-101: 6.3125rem;
--raw-font-size-98: 6.125rem; --raw-font-size-96: 6rem; --raw-font-size-94: 5.875rem;
--raw-font-size-92: 5.75rem; --raw-font-size-88: 5.5rem; --raw-font-size-82: 5.125rem;
--raw-font-size-80: 5rem; --raw-font-size-79: 4.9375rem; --raw-font-size-73: 4.5625rem;
--raw-font-size-72: 4.5rem; --raw-font-size-70: 4.375rem; --raw-font-size-64: 4rem;
--raw-font-size-59: 3.6875rem; --raw-font-size-58: 3.625rem; --raw-font-size-54: 3.375rem;
--raw-font-size-53: 3.3125rem; --raw-font-size-49: 3.0625rem; --raw-font-size-48: 3rem;
--raw-font-size-44: 2.75rem; --raw-font-size-42: 2.625rem; --raw-font-size-40: 2.5rem;
--raw-font-size-38: 2.375rem; --raw-font-size-36: 2.25rem; --raw-font-size-34: 2.125rem;
--raw-font-size-32: 2rem; --raw-font-size-30: 1.875rem; --raw-font-size-28: 1.75rem;
--raw-font-size-26: 1.625rem; --raw-font-size-24: 1.5rem; --raw-font-size-22: 1.375rem;
--raw-font-size-21: 1.3125rem; --raw-font-size-20: 1.25rem; --raw-font-size-19: 1.1875rem;
--raw-font-size-18: 1.125rem; --raw-font-size-17: 1.0625rem; --raw-font-size-16: 1rem;
--raw-font-size-15: 0.9375rem; --raw-font-size-14: 0.875rem;
--raw-leading-094: 0.94; --raw-leading-096: 0.96; --raw-leading-112: 1.12;
--raw-leading-130: 1.3; --raw-leading-150: 1.5; --raw-leading-155: 1.55;
--raw-tracking-003: 0.03em; --raw-tracking-006: 0.06em; --raw-tracking-008: 0.08em;
--raw-tracking-010: 0.1em; --raw-tracking-012: 0.12em; --raw-tracking-016: 0.16em;
--raw-radius-6: 0.375rem; --raw-radius-7: 0.4375rem; --raw-radius-10: 0.625rem; --raw-radius-11: 0.6875rem;
--raw-radius-13: 0.8125rem; --raw-radius-14: 0.875rem; --raw-radius-15: 0.9375rem; --raw-radius-17: 1.0625rem;
--raw-radius-20: 1.25rem; --raw-radius-21: 1.3125rem; --raw-radius-22: 1.375rem;
--raw-duration-fast: 150ms;
--raw-duration-normal: 250ms;
}
:root {
--background: var(--raw-color-ink-950);
--foreground: var(--raw-color-paper-50);
--foreground-muted: var(--raw-color-slate-200);
--foreground-soft: var(--raw-color-slate-250);
--foreground-subtle: var(--raw-color-slate-400);
--surface: var(--raw-color-ink-850);
--surface-inverse: var(--raw-color-paper-50);
--surface-poster: var(--raw-color-paper-200);
--on-inverse: var(--raw-color-ink-1000);
--on-poster: var(--raw-color-ink-975);
--accent: var(--raw-color-blue-500);
--accent-strong: var(--raw-color-blue-600);
--border-strong: var(--raw-color-white);
--header-scrim: var(--raw-gradient-header-scrim);
--header-shade: var(--raw-gradient-header-shade);
--hero-vignette: var(--raw-gradient-hero-vignette);
--hero-fade: var(--raw-gradient-hero-fade);
--hero-fade-compact: var(--raw-gradient-hero-fade-compact);
--card-fade: var(--raw-gradient-card-fade);
--elevation-card: var(--raw-shadow-card);
--elevation-wire: var(--raw-drop-shadow-wire);
--type-ghost-xl: var(--raw-font-size-380);
--type-ghost-lg: var(--raw-font-size-360);
--type-ghost-md: var(--raw-font-size-330);
--type-ghost-sm: var(--raw-font-size-300);
--type-wordmark: var(--raw-font-size-337);
--type-script-xl: var(--raw-font-size-170);
--type-script-lg: var(--raw-font-size-153);
--type-script-md: var(--raw-font-size-150);
--type-script-sm: var(--raw-font-size-128);
--type-script-xs: var(--raw-font-size-122);
--type-display: var(--raw-font-size-150);
--type-display-sm: var(--raw-font-size-120);
--type-step-number: var(--raw-font-size-120);
--type-stat-lg: var(--raw-font-size-140);
--type-stat: var(--raw-font-size-130);
--type-stat-ring: var(--raw-font-size-150);
--type-poster-title: var(--raw-font-size-132);
--type-statement: var(--raw-font-size-58);
--type-card-title: var(--raw-font-size-42);
--type-footer-logo: var(--raw-font-size-40);
--type-eyebrow: var(--raw-font-size-34);
--type-eyebrow-sm: var(--raw-font-size-30);
--type-bracket: var(--raw-font-size-30);
--type-button: var(--raw-font-size-26);
--type-row-title: var(--raw-font-size-26);
--type-stamp: var(--raw-font-size-26);
--type-logo: var(--raw-font-size-26);
--type-row-date: var(--raw-font-size-22);
--type-poster-meta: var(--raw-font-size-22);
--type-footer-link: var(--raw-font-size-22);
--type-button-sm: var(--raw-font-size-20);
--type-plate: var(--raw-font-size-20);
--type-meta: var(--raw-font-size-18);
--type-nav: var(--raw-font-size-18);
--type-fine: var(--raw-font-size-17);
--type-column: var(--raw-font-size-16);
--type-legal: var(--raw-font-size-15);
--type-body: var(--raw-font-size-16);
--type-caption: var(--raw-font-size-15);
--type-body-sm: var(--raw-font-size-14);
--type-poster-copy: var(--raw-font-size-14);
/* Figma snaps every text line to whole design pixels (15px × 1.5 → 23px). */
--line-copy: round(nearest, calc(var(--raw-leading-150) * 1em + 0.001rem), 0.0625rem);
--line-statement: round(nearest, calc(var(--raw-leading-094) * 1em + 0.001rem), 0.0625rem);
--line-row: round(nearest, calc(var(--raw-leading-112) * 1em + 0.001rem), 0.0625rem);
--line-meta: round(nearest, calc(var(--raw-leading-130) * 1em + 0.001rem), 0.0625rem);
--line-poster: round(nearest, calc(var(--raw-leading-155) * 1em + 0.001rem), 0.0625rem);
--letter-row: var(--raw-tracking-003);
--letter-nav: var(--raw-tracking-006);
--letter-address: var(--raw-tracking-008);
--letter-caps: var(--raw-tracking-010);
--letter-caps-wide: var(--raw-tracking-012);
--letter-column: var(--raw-tracking-016);
--corner-plate: var(--raw-radius-10);
--corner-sticker: var(--raw-radius-14);
--corner-card: var(--raw-radius-20);
--corner-art-xs: var(--raw-radius-7);
--corner-art-sm: var(--raw-radius-13);
--corner-art-md: var(--raw-radius-15);
--corner-art-lg: var(--raw-radius-17);
--corner-art-xl: var(--raw-radius-21);
--corner-art-2xl: var(--raw-radius-22);
--corner-art-tilt: var(--raw-radius-14);
--duration-hover: var(--raw-duration-normal);
--ease-entrance: cubic-bezier(0.2, 0, 0, 1);
}
@media (width < 1200px) {
:root {
--type-ghost-lg: var(--raw-font-size-280); --type-ghost-md: var(--raw-font-size-260); --type-ghost-sm: var(--raw-font-size-240);
--type-wordmark: var(--raw-font-size-248);
--type-script-xl: var(--raw-font-size-106); --type-script-lg: var(--raw-font-size-98); --type-script-md: var(--raw-font-size-96);
--type-script-sm: var(--raw-font-size-96); --type-script-xs: var(--raw-font-size-82);
--type-display: var(--raw-font-size-96); --type-display-sm: var(--raw-font-size-80);
--type-step-number: var(--raw-font-size-88);
--type-stat-lg: var(--raw-font-size-92); --type-stat: var(--raw-font-size-92); --type-stat-ring: var(--raw-font-size-101);
--type-poster-title: var(--raw-font-size-96);
--type-statement: var(--raw-font-size-44);
--type-card-title: var(--raw-font-size-34);
--type-footer-logo: var(--raw-font-size-36);
--type-eyebrow: var(--raw-font-size-28); --type-eyebrow-sm: var(--raw-font-size-28);
--type-button: var(--raw-font-size-24); --type-row-title: var(--raw-font-size-24);
--type-stamp: var(--raw-font-size-22); --type-row-date: var(--raw-font-size-21);
--type-poster-meta: var(--raw-font-size-20); --type-footer-link: var(--raw-font-size-20);
--type-button-sm: var(--raw-font-size-18); --type-nav: var(--raw-font-size-17);
--type-fine: var(--raw-font-size-18); --type-column: var(--raw-font-size-18); --type-legal: var(--raw-font-size-18);
--type-body: var(--raw-font-size-15); --type-body-sm: var(--raw-font-size-15);
--type-poster-copy: var(--raw-font-size-18);
--line-statement: round(nearest, calc(var(--raw-leading-096) * 1em + 0.001rem), 0.0625rem);
--line-poster: round(nearest, calc(var(--raw-leading-150) * 1em + 0.001rem), 0.0625rem);
--elevation-wire: var(--raw-drop-shadow-wire-compact);
}
}
@media (width < 896px) {
:root {
--corner-art-tilt: var(--raw-radius-11);
--type-ghost-lg: var(--raw-font-size-220); --type-ghost-md: var(--raw-font-size-200); --type-ghost-sm: var(--raw-font-size-190);
--type-wordmark: var(--raw-font-size-186);
--type-script-xl: var(--raw-font-size-79); --type-script-lg: var(--raw-font-size-73); --type-script-md: var(--raw-font-size-72);
--type-script-sm: var(--raw-font-size-72); --type-script-xs: var(--raw-font-size-73);
--type-display: var(--raw-font-size-72); --type-display-sm: var(--raw-font-size-72);
--type-step-number: var(--raw-font-size-80);
--type-stat-lg: var(--raw-font-size-64); --type-stat: var(--raw-font-size-64); --type-stat-ring: var(--raw-font-size-70);
--type-poster-title: var(--raw-font-size-72);
--type-statement: var(--raw-font-size-38);
--type-card-title: var(--raw-font-size-32);
--type-eyebrow: var(--raw-font-size-26); --type-eyebrow-sm: var(--raw-font-size-26);
--type-button: var(--raw-font-size-22); --type-row-title: var(--raw-font-size-22);
--type-row-date: var(--raw-font-size-20); --type-poster-meta: var(--raw-font-size-19);
--type-plate: var(--raw-font-size-19); --type-meta: var(--raw-font-size-17);
--type-fine: var(--raw-font-size-17); --type-column: var(--raw-font-size-17); --type-legal: var(--raw-font-size-17);
--type-poster-copy: var(--raw-font-size-16);
}
}
@media (width < 700px) {
:root {
--corner-art-tilt: var(--raw-radius-6);
--type-wordmark: var(--raw-font-size-94);
--type-script-xl: var(--raw-font-size-53); --type-script-lg: var(--raw-font-size-49); --type-script-md: var(--raw-font-size-48);
--type-script-sm: var(--raw-font-size-48); --type-script-xs: var(--raw-font-size-49);
--type-display: var(--raw-font-size-48); --type-display-sm: var(--raw-font-size-48);
--type-step-number: var(--raw-font-size-72);
--type-stat-lg: var(--raw-font-size-54); --type-stat: var(--raw-font-size-54); --type-stat-ring: var(--raw-font-size-59);
--type-poster-title: var(--raw-font-size-48);
--type-statement: var(--raw-font-size-30);
--type-card-title: var(--raw-font-size-30);
--type-footer-logo: var(--raw-font-size-32);
--type-eyebrow: var(--raw-font-size-22); --type-eyebrow-sm: var(--raw-font-size-22);
--type-button: var(--raw-font-size-20); --type-row-title: var(--raw-font-size-20);
--type-stamp: var(--raw-font-size-20); --type-row-date: var(--raw-font-size-18);
--type-poster-meta: var(--raw-font-size-17); --type-footer-link: var(--raw-font-size-18);
--type-plate: var(--raw-font-size-17); --type-meta: var(--raw-font-size-15);
--type-fine: var(--raw-font-size-15); --type-column: var(--raw-font-size-15); --type-legal: var(--raw-font-size-15);
--type-poster-copy: var(--raw-font-size-15);
}
}
```
Alpha colours used below, written out: `foreground/75` = `rgb(242 244 248 / .75)` (and so on for /5,
/3, /10, /14, /15, /16, /18, /20, /22, /28); `background/80` = `rgb(7 10 16 / .8)`, `background/40` =
`rgb(7 10 16 / .4)`; `on-inverse/60…/88` = `rgb(5 7 11 / …)`; `on-poster/85` = `rgb(7 9 13 / .85)`.
`drop-shadow-wire` = `filter: drop-shadow(var(--elevation-wire))`; `shadow-card` =
`box-shadow: var(--elevation-card)`. Rounded utilities: `md` 6 px, `lg` 8 px, `xl` 12 px, `2xl` 16 px.
### The four frames, the breakpoints and the rem grid
The page is drawn as four Figma frames and each viewport range renders **its own frame, scaled**:
| range | name | frame | viewport |
|---|---|---|---|
| desktop | `≥ 1200` | 1400 (Figma "Concept 16 — 1440") | `@media (width >= 1200px)` |
| laptop | `896 – 1199` | 1024 | `@media (896px <= width < 1200px)` |
| tablet | `700 – 895` | 768 | `@media (700px <= width < 896px)` |
| mobile | `< 700` | 390 | `@media (width < 700px)` |
Group names used below: **compact** = `< 1200` (laptop + tablet + mobile), **handheld** = `< 896`
(tablet + mobile). The four ranges never overlap.
**Every length in this document is in design px of the range's own frame. Write each one in CSS as
`rem = px / 16`.** The root font-size tracks the viewport so each frame renders 1:1 at its reference
width:
```css
html { font-size: 1.142857vw; } /* 16 · 100 / 1400 — keeps growing above 1400 */
@media (width < 1200px) { html { font-size: 1.5625vw; } } /* / 1024 */
@media (width < 896px) { html { font-size: 2.083333vw; } } /* / 768 */
@media (width < 700px) { html { font-size: min(4.102564vw, 19.2px); } } /* / 390, capped at 1.2× */
```
Mobile stops growing at a 468 px viewport (19.2 px rem); the mobile content column is
`max-width: 24.375rem` (390 design px) with `margin-inline: auto`, so wider phones get the 390 column
centred. Every compact section below carries that on mobile ("mobile column"). The hero is `100svh`
tall on every range. Where a value is in `vw` (the barbed wire on mobile) it is literal `vw`.
**Desktop layout model.** The 1400 frame is laid out as absolute layers: each desktop section is a
`position: relative` box of a fixed height and every element in it sits at `top/left` design px within
it (the coordinates below), exactly as in Figma. **Compact layout model.** The 1024 / 768 / 390 frames
are auto-layout: flex columns with paddings and gaps, given per section.
### Two layout helpers used everywhere
**`Rotated`** — how Figma exports a rotated layer: a **bounding box** (positioned and sized per range)
that is `display:flex; align-items:center; justify-content:center`, holding the un-rotated content as a
`display:block; flex:none` child that carries the `rotate`. Every "box … rotate …" below is this
structure. With **parallax** (see Motion) the box is the static measured layer and an inner
`display:flex; width/height:100%; align-items:center; justify-content:center; will-change:transform`
layer between box and content takes the parallax transform. Default parallax tilt is
`0.3° per rem` of travel unless a tilt is given.
**`Vector`** — a decorative SVG/PNG layer: `<span aria-hidden style="position:relative; display:block;
width; height">` (the layer box) holding `<span style="position:absolute; inset:…">` (the art inset —
Figma bleeds strokes past the layer, so insets are negative percentages, `top/bottom` first then
`left/right`, default `0`) holding a `<picture>` with per-range art:
```html
<picture>
<source media="(width < 700px)" srcset="…-mobile.svg"> <!-- only if a mobile file exists -->
<source media="(width < 896px)" srcset="…-tablet.svg"> <!-- only if a tablet file exists -->
<source media="(width < 1200px)" srcset="…-laptop.svg"> <!-- only if a laptop/compact file exists -->
<img src="….svg" alt="" style="display:block; width:100%; height:100%">
</picture>
```
Sources fall through: a layer that only has a "laptop/compact" file uses it on every compact range.
These vector `<img>`s are eager (no `loading` attribute). Every raster card/wire/arc image other than
the hero photo and the preloader cards is `loading="lazy"` and covers its box (`position:absolute;
inset:0; width:100%; height:100%`, `object-fit` as stated).
**`CardImage`** — a collectible card: a reveal wrapper `<div style="width:fit-content">` (motion kind
**card**, see Motion) around the card box `<div style="position:relative; overflow:hidden; width;
height; border-radius">`. The image inside is `object-fit: cover`. With a **crop**, the image instead sits
in an absolutely positioned crop box (`top/left/height/width` in %, given per card) at `object-fit: cover`.
## Motion engine you write
### Springs (react-spring 10 semantics)
Integrate exactly like react-spring: 1 ms sub-steps, `springForce = −tension × 0.000001 × (pos − to)`,
`dampingForce = −friction × 0.001 × vel`, `vel += (springForce + dampingForce) / mass`, `pos += vel`
(mass 1); at rest when `|vel| ≤ restVelocity` and `|to − pos| ≤ precision`
(`precision = min(1, |to − from| × 0.001)`, `restVelocity = precision / 10`). Retargeting keeps the
velocity. A `delay` (ms) holds the spring at its current value before it starts toward the target.
A style map with string values (`"translateY(18%) scale(1.18)"`, `"blur(6px)"`, a gradient) animates as
**one progress spring `p: 0 → 1`** and lerps every number in the strings by `p` (react-spring's
`AnimatedString`); numeric keys (`opacity`) use the same config and start together, so one `p` per element
drives the whole map. Overshoot extrapolates past the target (scale under 1, rotation past 0) — that is
the "slap". A **keyframe chain** (`to: [step1, step2, step3]`, each step with its own config) plays its
steps in sequence, each from where the last one rested. `immediate` jumps to the value.
**Reduced motion** (`prefers-reduced-motion: reduce`, watched live) makes every spring immediate.
```js
// ticker.add(fn): fn(now) runs every frame until it returns false (see "One shared ticker").
function spring({ from = 0, to = 1, tension = 170, friction = 26, delay = 0, onUpdate, onRest }) {
let pos = from, vel = 0, target = to, startAt = performance.now() + delay, last = null, done = false, running = false;
const reduce = () => matchMedia("(prefers-reduced-motion: reduce)").matches;
const run = () => { if (!running) { running = true; ticker.add((now) => (running = step(now))); } };
const s = {
get value() { return pos; },
set(v, { delay: d = 0, immediate = false, config } = {}) {
if (config) ({ tension, friction } = config);
target = v; from = pos; startAt = performance.now() + d; done = false; last = null; // velocity is kept
if (immediate || reduce()) { pos = v; vel = 0; onUpdate?.(pos); done = true; onRest?.(); }
else run();
},
};
function step(now) {
if (done) return false;
if (now < startAt) return true;
const dt = Math.min(64, last == null ? 16 : now - last); last = now;
const precision = Math.min(1, Math.abs(target - from) * 0.001) || 0.001, restV = precision / 10;
for (let i = 0; i < Math.ceil(dt); i++) {
const f = -tension * 0.000001 * (pos - target) - friction * 0.001 * vel;
vel += f; pos += vel;
}
if (Math.abs(vel) <= restV && Math.abs(target - pos) <= precision) { pos = target; vel = 0; done = true; }
onUpdate?.(pos);
if (done) onRest?.();
return !done;
}
if (reduce()) { pos = to; onUpdate?.(pos); } else run();
return s;
}
```
### One shared ticker
A single app-wide `requestAnimationFrame` loop. Every per-frame job (springs, parallax measurement,
the preloader's progress poll) subscribes to it; each subscriber has its own minimum gap in ms (the
parallax/scroll trigger polls at **10 ms**). The loop is reference-counted: it stops when the last
subscriber leaves. A subscriber that throws is isolated (logged) and never kills the loop. Lenis runs
on its own rAF.
### Gated reveals — "seen"
Nothing reveals until the preloader **releases** (350 ms after the stamp lands, as the curtain starts
to lift — see the loader). From then on, each reveal element is watched by two IntersectionObservers:
- `deep` with `rootMargin: "0px 0px -15% 0px"` — intersecting ⇒ **seen** (the viewer has actually
reached it: its top is 15 % above the viewport bottom).
- `any` with no margin — while it is on screen, if the page is scrolled to the bottom
(`scrollY + innerHeight >= scrollHeight − 4`, re-checked on scroll) ⇒ **seen** (for elements at the very
end that can't climb that high).
A reveal plays its **in** spring (with its delay) once seen **and** intersecting, plays once, and stays.
A `repeat` reveal (the FAQ answers) resets to its *from* state **immediately** when it leaves the
viewport (or is hidden) and replays on the next entry. A tagless reveal (the scribbles, which start at
zero visible extent) is observed through an unscaled `display:block` wrapper span.
**Replay on range change.** Crossing into another of the four ranges (resizing, rotating a tablet)
resets every reveal to its *from* state and plays them again for the new layout — those on screen at
once, the rest as they scroll in.
### The motion set — one spec per element kind (verbatim)
```js
const PUNCH = { tension: 420, friction: 22 };
const SOFT = { tension: 120, friction: 26 };
const HEAVY = { tension: 60, friction: 30 };
const DRIP = { tension: 140, friction: 13 };
// Spray-can reveal: a soft-edged mask sweeps left→right while the paint settles from a blur.
const sprayMask = (edge) => `linear-gradient(90deg, rgba(0, 0, 0, 1) ${edge}%, rgba(0, 0, 0, 0) ${edge + 22}%)`;
const SPRAY_FROM = { filter: "blur(6px)", maskImage: sprayMask(-22) };
const SPRAY_TO = { filter: "blur(0px)", maskImage: sprayMask(100) };
// A real rubber stamp: lifted and tilted above the page, it drops, squashes on impact (ink "blooms"
// from a blur), then settles. Played as a keyframe chain.
const STAMP_FROM = { opacity: 0, transform: "translateY(-55%) scale(2.1) rotate(-14deg)", filter: "blur(3px)" };
const STAMP_TO = [
{ opacity: 0.6, transform: "translateY(-30%) scale(1.6) rotate(-9deg)", filter: "blur(2px)", config: { tension: 260, friction: 26 } },
{ opacity: 1, transform: "translateY(0%) scale(0.9) rotate(1deg)", filter: "blur(0px)", config: { tension: 900, friction: 30 } },
{ opacity: 1, transform: "translateY(0%) scale(1) rotate(0deg)", filter: "blur(0px)", config: { tension: 400, friction: 18 } },
];
const MOTION = {
// Headlines: each line punches in with a scale overshoot, like a poster stamp.
headline: { type: "inview", from: { opacity: 0, transform: "translateY(18%) scale(1.18)" }, to: { opacity: 1, transform: "translateY(0%) scale(1)" }, config: PUNCH, stagger: 110 },
// Graffiti words, then their underlines: sprayed on.
mark: { type: "inview", from: SPRAY_FROM, to: SPRAY_TO, config: { tension: 55, friction: 20 }, delay: 300 },
scribble: { type: "inview", from: SPRAY_FROM, to: SPRAY_TO, config: { tension: 70, friction: 20 }, delay: 700 },
// Paragraphs: lines slide up from under a mask, one after another.
copy: { type: "text", layer: "line", out: { transform: "translateY(100%)" }, in: { transform: "translateY(0%)" }, config: { tension: 170, friction: 26 }, stagger: 70, overflow: true },
// Buttons drop in; their drips run down with an overshoot.
button: { type: "inview", from: { opacity: 0, transform: "translateY(-30%)" }, to: { opacity: 1, transform: "translateY(0%)" }, config: SOFT },
drip: { type: "inview", from: { transform: "scaleY(0)" }, to: { transform: "scaleY(1)" }, config: DRIP, delay: 350, stagger: 70, style: { transformOrigin: "50% 0%" } },
// Hero vault photo: the lights come on as it eases back.
photo: { type: "inview", from: { transform: "scale(1.16)", filter: "brightness(0.25)" }, to: { transform: "scale(1)", filter: "brightness(1)" }, config: HEAVY },
// Every card: slapped down onto the table.
card: { type: "inview", from: { opacity: 0, transform: "scale(1.35) rotate(-8deg)" }, to: { opacity: 1, transform: "scale(1) rotate(0deg)" }, config: PUNCH, stagger: 90 },
chip: { type: "inview", from: { opacity: 0, transform: "scale(1.4)" }, to: { opacity: 1, transform: "scale(1)" }, config: PUNCH, stagger: 70 },
step: { type: "inview", from: { opacity: 0, transform: "scale(1.12)" }, to: { opacity: 1, transform: "scale(1)" }, config: PUNCH, stagger: 100 },
sticker: { type: "inview", from: { opacity: 0, transform: "scale(1.5) rotate(-12deg)" }, to: { opacity: 1, transform: "scale(1) rotate(0deg)" }, config: { tension: 420, friction: 18 }, stagger: 120 },
// Ledger rows fade in; each stamp is pressed like a real one.
ledger: { type: "inview", from: { opacity: 0 }, to: { opacity: 1 }, config: SOFT, stagger: 70 },
stamp: { type: "inview", from: STAMP_FROM, to: STAMP_TO, config: SOFT, delay: 200, stagger: 160 },
// FAQ answers drop open each time.
faq: { type: "inview", from: { opacity: 0, transform: "translateY(-30%)" }, to: { opacity: 1, transform: "translateY(0%)" }, config: SOFT, repeat: true },
// Footer HELD-MINT outline: letters stand up one by one.
wordmark: { type: "text", layer: "letter", out: { y: 80, opacity: 0 }, in: { y: 0, opacity: 1 }, config: { tension: 200, friction: 22 }, stagger: 45 },
};
```
An **inview** element starts at its `from` styles (set inline before first paint), and plays to `to`
with `delay = (spec.delay ?? 0) + index × (spec.stagger ?? 0)`, where `index` is its position in its
group (given per element below; 0 when not given). `maskImage` is the CSS `mask-image` (set
`-webkit-mask-image` too). The `transform` of a reveal composes with any positioning `translate` /
`rotate` the element already has — keep positioning in the individual `translate`/`rotate`/`scale` CSS
properties and the reveal in `transform`.
A **text** element is split by you, in place:
- **line** layer (`copy`): split into words (keep inline children such as the accent `<span>` as
their own styled words), lay them out as a `display:flex; flex-wrap:wrap; white-space:pre-wrap`
row whose `column-gap` equals the **measured width of a space** in that element's font (measure
`"x x"` minus `"xx"` in a hidden probe, divide by the font-size, use `em`), so lines break as in the
design. Group the words by line (same `offsetTop`), wrap each line in a clip span
(`display:inline-block; overflow:hidden; padding-block:.12em; margin-block:-.12em`) and translate the
inner line `translateY(100%) → translateY(0%)` with `{170, 26}`, line `i` delayed
`index × 120 + i × 70` ms. A `text-align:center` element gets `justify-content:center` on its rows.
Re-split on resize.
- **letter** layer (`wordmark`): each letter its own `display:inline-block` span starting at
`translateY(80px)` and `opacity 0`, springing to `0 / 1` with `{200, 22}`, letter `i` delayed `i × 45` ms.
No clipping.
Keep a visually hidden copy of the full string for screen readers (`aria-hidden` on the split copy).
### Scroll parallax
A parallax layer is the `Rotated` box (static, measured) plus its inner moving layer. Every 10 ms while
the box is on screen (and for 10 more ticks after it leaves) measure the box and compute
```js
const r = box.getBoundingClientRect(), vh = window.innerHeight;
const progress = Math.min(Math.max(0, (vh - r.top) / (vh + r.height)), 1); // "top bottom" → "bottom top"
const offset = reducedMotion ? 0 : progress - 0.5; // centred: mid-viewport = the designed place
ySpring.set(offset * distance); // rem
rSpring.set(offset * tilt); // deg
// inner.style.transform = `translate3d(0, ${y}rem, 0) rotate(${r}deg)`
```
with both springs at `{ tension: 280, friction: 40 }`. Positive `distance` drifts down as the page scrolls
(reads as further away); negative drifts up (closer, faster). Reduced motion snaps both to 0. The layers,
with `distance` (rem) and `tilt` (°):
| layer | distance | tilt |
|---|---|---|
| hero vault photo | 24 | 0 |
| hero desktop left card | −14 | −4.2 |
| hero compact card | −8 | −2.4 |
| intro tucked card | 6 | 1.8 |
| intro front card | −12 | −3.6 |
| barbed wire 1 | 10 | 3 |
| barbed wire 2 | −8 | −2.4 |
| vault fan: far left / far right / left / right / centre | 10 / 7 / −4 / −7 / −12 | −3 / 3 / −2.5 / 2.5 / 1 |
| drop card | −10 | −3 |
| drop wire arcs 1 / 2 / 3 / 4 | 8 / −6 / 12 / −9 | 2.4 / −1.8 / 3.6 / −2.7 |
| join card | −10 | −3 |
### Hover — colour swaps
Hover/focus colour changes are CSS transitions: `transition: background-color, color, filter
var(--duration-hover) var(--ease-entrance)` (250 ms, `cubic-bezier(0.2, 0, 0, 1)`).
## Shared components
### Eyebrow (crown + hand-written label)
`<div class="eyebrow">`: compact `display:flex; align-items:center; gap:12`; desktop
`display:contents` and both children become absolute tilted layers at the section's coordinates
(`crownBox`, `labelBox`, given per section).
- **Crown**: `Rotated` box (compact `position:relative; flex-shrink:0`), rotate desktop `8deg`
(compact upright), holding a `Vector` of `shared/crown-label.svg` (laptop `shared/crown-label-laptop.svg`,
tablet `shared/crown-label-tablet.svg`, mobile `shared/crown-label-mobile.svg`), size desktop 31 × 38
(h × w), laptop `1.73125rem × 2.15625rem`, tablet `1.6375rem × 2.0375rem`, mobile `1.45rem × 1.8rem`;
inset desktop/laptop/tablet `-4.05% -3.26%` (laptop/tablet `-4.06% -3.26%`), mobile `-4.31% -3.47%`.
- **Label**: `Rotated` box (compact `position:relative`), rotate desktop `-3deg`, holding
`<p class="type-script" style="font-size:var(--type-eyebrow); color:var(--accent)">` — lowercase copy.
### Headline marks and scribbles
Every section headline is `type-display` (900 italic uppercase, line-height 1) in `--foreground`, one
line per span. The graffiti **mark** is a `type-script` span in `--accent` (the drop poster uses
`--accent-strong`), `text-transform:none; font-style:normal`, inside a `Rotated` box at `-4deg`
(process: `-5deg`); on compact the mark also gets `translate: 0 -0.04em`. Under it a **scribble**:
a `Rotated` box at `-3deg` holding a `Vector` 5 px tall (`h-1.25`) at the given width with per-range SVGs.
On compact the mark + scribble live in a fixed-size `position:relative; flex-shrink:0` **holder** that
flows inline after the second headline line; the mark and scribble boxes are absolute inside it.
Compact headline structure (all sections): `<h2 class="type-display" style="display:flex;
flex-direction:column; width:100%; font-size:var(--type-display)">` → line 1 `<span style="display:block">`
with a negative bottom margin (laptop −13, tablet −10, mobile −7) → a row `<span style="display:flex;
flex-wrap:wrap; align-items:center; column-gap: laptop 17 / tablet 13 / mobile 9">` holding line 2 and the
holder. Desktop: `display:block`, every span absolute at its coordinates.
Motion: line 1 = **headline** index 0, line 2 = **headline** index 1, the mark = **mark**, the scribble
= **scribble** (through a `display:block` wrapper span).
### GhostWord
A huge 5 %-opacity script word behind a heading: an absolute, `pointer-events:none` `Rotated` box holding
`<span aria-hidden class="type-script" style="color:rgb(242 244 248 / .05); font-size:…">`. Static.
### Brackets
`[` and `]` are `<span aria-hidden class="type-caps" style="font-size:var(--type-bracket);
color:var(--accent)">` around a short paragraph. Compact: a `display:flex; align-items:flex-start;
gap:10` row `[ · p(flex:1) · ]`, the closing `]` is `display:flex; width:10; align-self:stretch;
align-items:flex-end` (bottom-aligned). Desktop: all three absolute.
### PlateButton — the "RS Button" (paint plate + drips)
An `<a class="plate">` (`position:relative; display:block; width:fit-content`), motion **button**. Inside:
1. **Offset** `<span aria-hidden>`: `position:absolute; background:var(--accent)`, same width as the plate,
at a small down-right offset.
2. **Drips**: seven `<span aria-hidden>` `position:absolute; background:var(--accent);
border-radius: 0 0 9999px 9999px`, hanging from just above the plate's bottom edge, each motion
**drip** with its own index 0…6 (`delay 350 + i × 70`, `scaleY 0 → 1` from the top).
3. **Plate** `<span class="type-display">`: `position:relative; display:flex; align-items:center;
justify-content:space-between; background:var(--surface-inverse); color:var(--on-inverse)`, the label
then an arrow `Vector` (`shared/icon-arrow.svg`, compact `shared/icon-arrow-compact.svg`; 16 tall,
desktop 26 wide, compact 24; inset desktop `-9.38% -5.77%`, compact `-9.38% -6.25%`).
**Hover / keyboard focus inverts it** (the 250 ms swap): plate → `background:var(--accent);
color:var(--foreground)`; offset and drips → `var(--surface-inverse)`; the ink arrow →
`filter: invert(1)`.
Sizes (h = height):
| | root h / padding-right | plate h · padding · font | offset top/left · h | drip top |
|---|---|---|---|---|
| `md` desktop | 65 / 7 | 58 · `0 26` · `--type-button` | 7/7 · 58 | 63 |
| `md` laptop | 62 / 6 | 56 · `0 22` | 6/6 · 56 | 60 |
| `md` handheld | 58 / 6 | 52 · `0 22` | 6/6 · 52 | 56 |
| `sm` desktop | 49 / 5 | 44 · `0 16 0 18` · `--type-button-sm` | 5/5 · 44 | 47 |
| `sm` compact | 46 / 6 | 40 · `0 22` | 6/6 · 40 | 44 |
Desktop drips have per-button `left · height · width`; every compact range draws the same seven sizes
(h × w): `18×6, 10×4, 30×7, 13×4, 22×6, 8×4, 16×7`, only their `left`s move. Each button's plate widths
and drip lists are given where it appears.
### SprayLink — the sprayed underline on hover
A `<span style="position:relative; display:block">` holding the link and an inline SVG underline
(`viewBox="0 0 75.2 21.1" preserveAspectRatio="none" overflow="visible" fill="currentColor"`,
`position:absolute; pointer-events:none; color:var(--accent)`, positioned per use). At rest it is
`opacity:0`. Its paths, verbatim (Figma "header-spray-underline", 75.2 × 21.1):
```js
const LINE = "M8.6 8.1C17.4 4.6 38.1 2.9 67.6 4.1C69.6 4.3 69.6 8.5 67.6 8.9C44 7.7 23.4 10.3 11.6 12.7C9 13.1 7.2 9.3 8.6 8.1Z";
const DRIPS = [
"M23.4 8.6C23.4 7.77157 22.7284 7.1 21.9 7.1C21.0716 7.1 20.4 7.77157 20.4 8.6V14.6C20.4 15.4284 21.0716 16.1 21.9 16.1C22.7284 16.1 23.4 15.4284 23.4 14.6V8.6Z",
"M43.7 8.4C43.7 7.68203 43.118 7.1 42.4 7.1C41.682 7.1 41.1 7.68203 41.1 8.4V19.8C41.1 20.518 41.682 21.1 42.4 21.1C43.118 21.1 43.7 20.518 43.7 19.8V8.4Z",
"M59.2 8.2C59.2 7.59249 58.7075 7.1 58.1 7.1C57.4925 7.1 57 7.59249 57 8.2V13C57 13.6075 57.4925 14.1 58.1 14.1C58.7075 14.1 59.2 13.6075 59.2 13V8.2Z",
];
const DOTS = [
"M3.6 7.1C4.15229 7.1 4.6 6.65228 4.6 6.1C4.6 5.54772 4.15229 5.1 3.6 5.1C3.04772 5.1 2.6 5.54772 2.6 6.1C2.6 6.65228 3.04772 7.1 3.6 7.1Z",
"M5.1 14.3C5.4866 14.3 5.8 13.9866 5.8 13.6C5.8 13.2134 5.4866 12.9 5.1 12.9C4.7134 12.9 4.4 13.2134 4.4 13.6C4.4 13.9866 4.7134 14.3 5.1 14.3Z",
"M0.6 10.7C0.931371 10.7 1.2 10.4314 1.2 10.1C1.2 9.76863 0.931371 9.5 0.6 9.5C0.26863 9.5 0 9.76863 0 10.1C0 10.4314 0.26863 10.7 0.6 10.7Z",
"M71.6 3.5C72.0971 3.5 72.5 3.09706 72.5 2.6C72.5 2.10294 72.0971 1.7 71.6 1.7C71.1029 1.7 70.7 2.10294 70.7 2.6C70.7 3.09706 71.1029 3.5 71.6 3.5Z",
"M74.6 8.2C74.9314 8.2 75.2 7.93137 75.2 7.6C75.2 7.26863 74.9314 7 74.6 7C74.2686 7 74 7.26863 74 7.6C74 7.93137 74.2686 8.2 74.6 8.2Z",
"M63.6 13.3C63.9866 13.3 64.3 12.9866 64.3 12.6C64.3 12.2134 63.9866 11.9 63.6 11.9C63.2134 11.9 62.9 12.2134 62.9 12.6C62.9 12.9866 63.2134 13.3 63.6 13.3Z",
"M33.4 14.7C33.7314 14.7 34 14.4314 34 14.1C34 13.7686 33.7314 13.5 33.4 13.5C33.0686 13.5 32.8 13.7686 32.8 14.1C32.8 14.4314 33.0686 14.7 33.4 14.7Z",
"M49.9 1.2C50.2314 1.2 50.5 0.931371 50.5 0.6C50.5 0.268629 50.2314 0 49.9 0C49.5686 0 49.3 0.268629 49.3 0.6C49.3 0.931371 49.5686 1.2 49.9 1.2Z",
"M15.7 2.3C16.0866 2.3 16.4 1.9866 16.4 1.6C16.4 1.2134 16.0866 0.9 15.7 0.9C15.3134 0.9 15 1.2134 15 1.6C15 1.9866 15.3134 2.3 15.7 2.3Z",
];
```
Pointer enter on the wrapper (or focus on the link) sprays it on; leave/blur takes it off:
- layer `opacity` → 1 at once (on) / springs → 0 with `{260, 30}` (off);
- the line's `clip-path` `inset(0% 100% 0% 0%) → inset(0% 0% 0% 0%)` with `{300, 32}` — a left-to-right
stroke;
- each drip path `transform: scaleY(0) → scaleY(1)` (`transform-box: fill-box; transform-origin: 50% 0%`),
delay `160 + i × 70`, `{110, 9}` — paint running down with an overshoot;
- each dot `scale(0) → scale(1)` (`transform-box: fill-box; transform-origin: 50% 50%`), delay
`(i × 37) % 180` (= 0, 37, 74, 111, 148, 5, 42, 79, 116 ms), `{420, 16}` — overspray popping.
- **Off**: the line, drips and dots snap back to their unsprayed state immediately (no spring), so the
underline vanishes at once.
The link text itself swaps to `--accent` on hover with the 250 ms transition.
## The loader / reveal — "Pile + stamp" (the hero moment)
A black curtain over everything from the very first paint. Four cards from the site slap down onto the
table one by one as the page loads — overlay card, tucked card, the drop card, the front card — while a
huge zero-padded counter climbs `000 → 100` and a blue bar fills beside it. At 100 a blue **SEALED**
stamp slams onto the middle of the pile; 350 ms later the whole curtain lifts off upward and the page's
own reveals start under it (the vault photo's lights come on, the headline punches in).
**Markup** (first thing in the page; present in the served HTML, never injected late):
```html
<div data-preloader role="status" aria-label="Loading" class="preloader">
<style>html{overflow:hidden}</style> <!-- the scroll lock leaves with the curtain -->
<noscript><style>[data-preloader]{display:none!important}html{overflow:auto!important}</style></noscript>
<div aria-hidden class="preloader-grain"></div>
<p class="type-script preloader-caption">card custody · zug</p>
<div class="preloader-stage"><div class="preloader-box">
<!-- four card slots, then the stamp -->
<span class="type-display preloader-stamp">Sealed</span>
</div></div>
<div class="preloader-count"><p class="type-display">000</p><div class="bar"><div class="fill"></div></div></div>
</div>
```
**Curtain**: `position:fixed; inset:0; z-index:70; display:flex; flex-direction:column; overflow:hidden;
background:var(--background); --edge: max(4.5svh, 18px); padding: var(--edge) 5vw;
transform: translateY(calc(-1 × v × 105%))` (the lift, below). Grain: the page's grain layer exactly
(`position:absolute; inset:0; pointer-events:none; background:url(shared/texture-grain.webp) 0 0/32rem;
opacity:.2; mix-blend-mode:screen`).
**Caption**: `position:relative; flex-shrink:0; align-self:center; font-size:clamp(18px, 3.4vmin, 2.25rem);
white-space:nowrap; color:var(--accent)` — "card custody · zug". It sits as far from the top as the
bar sits from the bottom (`--edge`).
**Stage**: `position:relative; margin-top:2svh; margin-bottom:2.5svh` (landscape `3svh`);
`display:flex; flex:1; min-height:0; align-items:center; justify-content:center; container-type:size`.
The **composition box** inside is fitted into it with container units:
| layout | media | aspect | width |
|---|---|---|---|
| wide | default (landscape) | `1550 / 870` | `min(100cqw, 178cqh)` |
| portrait | `(orientation: portrait)` | `1000 / 1300` | `min(100cqw, 77cqh)` |
| tall | `(max-aspect-ratio: 5/8)` — wins over portrait | `1000 / 1900` | `min(100cqw, 52.6cqh)` |
A wide spread on landscape screens, a 2×2 pile on portrait tablets, a zig-zag column on tall phones —
the box always fills the screen's height. Track which layout is showing with the same two
`matchMedia` queries (tall first, then portrait, else wide) and re-target the cards on change.
**Cards** (back → front = their landing order). Each is a wrapper `position:absolute;
translate:-50% -50%` at `left/top/width` in % of the box, holding the animated card `position:relative;
aspect-ratio:3/4; width:100%; filter:drop-shadow(var(--elevation-wire))`, holding the image
(`object-fit:contain`, `loading="eager" fetchpriority="low"`, `alt=""`):
| # | image | lands at | wide: left / top / w · tilt | portrait | tall |
|---|---|---|---|---|---|
| 1 | `intro/intro-card-tucked-overlay.png` | 0.05 | 19.7 / 40.8 / 28.2 · −20° | 30 / 27 / 47 · −12° | 36 / 22 / 62 · −10° |
| 2 | `intro/intro-card-tucked.png` | 0.30 | 58.7 / 43.7 / 24.6 · 2° | 71 / 26 / 41 · 6° | 68 / 40 / 52 · 7° |
| 3 | `drop/drop-card.png` | 0.55 | 40.2 / 68.6 / 19.2 · 4° | 31 / 71 / 37 · 4° | 32 / 60 / 46 · −4° |
| 4 | `intro/intro-card-front.png` | 0.78 | 77.1 / 53.4 / 32.8 · 20° | 67 / 68 / 51 · 12° | 64 / 77 / 64 · 10° |
Card 1's art has white corners baked in, so its image sits in an `overflow:hidden;
border-radius:4.8% / 3.6%` clip. Each card is a spring with **`SLAP = { tension: 420, friction: 20 }`**:
not landed (`target < at`) → `opacity:0; transform: scale(1.6) rotate(${tilt − 10}deg)`; landed →
`opacity:1; transform: scale(1) rotate(${tilt}deg)` — an overshooting scale, quick settle.
**Stamp**: `position:absolute; top:50%; left:50%; translate:-50% -50%; border-radius:.12em;
border:.09em solid var(--accent); background:rgb(7 10 16 / .4); padding:.06em .28em;
white-space:nowrap; color:var(--accent)`, `type-display` (so it reads **SEALED**), font-size
`calc(min(100cqw, 178cqh) * 0.09)` (portrait `calc(min(100cqw, 77cqh) * 0.15)`, tall
`calc(min(100cqw, 52.6cqh) * 0.2)`). Spring `{ tension: 520, friction: 22 }`: hidden
`opacity:0; transform: scale(2.6) rotate(-24deg)` → done `opacity:1; transform: scale(1) rotate(-8deg)`.
**Counter + bar**: `position:relative; display:flex; flex-shrink:0; align-items:flex-end; gap:3vw`;
in landscape it overlays the stage's lower-left corner: `position:absolute; left:5vw; right:5vw;
bottom:var(--edge)` (portrait keeps it in flow below the pile). The counter
`<p class="type-display" style="font-size:clamp(44px, 13vmin, 8rem); line-height:.8;
color:var(--foreground)">` shows `String(Math.round(p * 100)).padStart(3, "0")`. The bar
`flex:1; height:.6vmin; min-height:0.25rem; background:rgb(242 244 248 / .15)` holds a fill
`height:100%; transform-origin:left; background:var(--accent); transform:scaleX(p)`.
**Progress** — real image loading blended with a minimum run time, so even a cached page shows the whole
animation:
```js
const MIN_MS = 1500, EXIT_MS = 750, LIFT_DELAY = 350, SETTLE = 175;
const STOPS = [0.05, 0.3, 0.55, 0.78]; // each card's landing
const progress = spring({ from: 0, to: 0, tension: 480, friction: 52, onUpdate: renderCounterAndBar });
stopScroll(); // Lenis stop + html lock
const t0 = performance.now(); let last = 0, target = 0;
function tick() {
// Lazy images below the fold never load while scroll is locked — count eager ones only,
// and give up waiting after 2× the minimum run time.
const imgs = [...document.images].filter((i) => i.loading !== "lazy");
const loaded = imgs.length ? imgs.filter((i) => i.complete).length / imgs.length : 1;
const elapsed = performance.now() - t0;
const time = Math.min(1, elapsed / MIN_MS);
const next = elapsed > MIN_MS * 2 ? 1 : Math.min(time, 0.15 + loaded * 0.85);
progress.set(next);
if (next >= 1 || STOPS.some((at) => at > last && at <= next)) { last = next; target = next; landCards(target); }
if (next >= 1) { setTimeout(onDone, SETTLE); return; }
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
```
The cards re-target only when a stop is crossed (and at 1); the counter and bar follow the progress
spring every frame.
**Sequence after 100**: `SETTLE` (175 ms) after the target reaches 1 → **done**: the stamp springs in;
`LIFT_DELAY` (350 ms) after done → **release** (the page's reveal observers attach — see Motion) and the
lift spring `v: 0 → 1` with **`{ tension: 480, friction: 48 }`** starts (`translateY(−v × 105%)`, the
curtain leaves upward); `EXIT_MS` (750 ms) after done → the curtain is removed from the DOM (its `<style>`
lock goes with it) and scroll starts again. Total intro ≈ 1.5 s on a cached page. The page under the
curtain is fully laid out and painted (the hero photo is the LCP and loads at high priority under it).
## The header (fixed, over every block)
`<header>`: `position:fixed; left:0; right:0; top:0; z-index:25`. Desktop: `height:97;
background-image:var(--header-scrim)`, every child absolute. Compact: `display:flex;
align-items:center; justify-content:space-between; padding-block:14`, padding-inline laptop 32,
tablet 40, mobile 20.
- **Backdrop** (fades in once the page scrolls, so the nav stays legible over any section): `position:absolute;
inset:0; z-index:-1; pointer-events:none; border-bottom:1px solid rgb(242 244 248 / .1);
background:rgb(7 10 16 / .8); backdrop-filter:blur(12px)`; desktop `bottom:auto; height:75`. Opacity
springs `0 ↔ 1` with `{ tension: 220, friction: 30 }` on `window.scrollY > 40`.
- **Shade** (compact only): `position:absolute; left:0; right:0; top:0; z-index:-1; height:130;
pointer-events:none; background-image:var(--header-shade)`.
- **Logo** `<a href="/" aria-label="HELD-MINT">`: `position:relative; display:flex; align-items:center;
gap:9`; desktop `position:absolute; top:12; left:31; display:block; height:47; width:173`.
- Mark: a span `display:flex; align-items:center; justify-content:center` (desktop
`position:absolute; top:16; left:23; height:22; width:23`) with a `Vector` of
`header/header-logo-mark.svg` (compact `header/header-logo-mark-compact.svg`), 22 tall, desktop 19
wide and `transform: skewX(-11.31deg)`, compact 24 wide.
- Wordmark: `<span class="type-display" style="font-size:var(--type-logo)">HELD-MINT</span>`, white on
desktop (`position:absolute; top:18; left:52; text-box: trim-both cap alphabetic`),
`--foreground` on compact.
- **Right group**: desktop `display:contents`; compact `display:flex; align-items:center`, gap laptop
34, tablet 18. In order:
- **Locale** `<button>` "En": `type-caps`, `font-size:var(--type-nav); letter-spacing:var(--letter-nav);
color:rgb(242 244 248 / .75); display:flex; align-items:center`, desktop `line-height:var(--line-copy);
position:absolute; top:24; left:380; gap:4`, laptop gap 5; hidden on handheld. Then a
`header/icon-chevron.svg` Vector `0.97222rem` square.
- **Nav** `<nav aria-label="Main">`: desktop `position:absolute; top:12; left:50%;
translate:-50% 0`; hidden on handheld. `<ul>` `display:flex; align-items:center; gap:34`, desktop
`padding:12 39`. Items, each a **SprayLink** with the same caps text style (75 % white, hover →
`--accent`): **Marketplace** `#` · **Vault** `#vault` (`aria-current="page"`) · **White Paper** `#` ·
**Contact Us** `#`. Underline box: `left:-17; height:21`; desktop `top:25; width:calc(100% + 1.952rem)`;
laptop `top:15; width:calc(100% + 2.104rem)`.
- **Log In** `<a href="#">`: same caps style, `display:flex; align-items:center`, desktop
`position:absolute; top:24; left:977; gap:4`, laptop gap 5; hidden on handheld; then a
`header/icon-user.svg` Vector `0.97222rem` square, inset `0 -1.57% -6.1% 0`.
- **Buy NFT** PlateButton `sm`, href `#`: plate width desktop 133, compact 139; desktop
`position:absolute; top:12; left:1230`; hidden on mobile. Desktop drips (left · h · w):
`16·15·5, 32·8·4, 50·27·6, 68·11·4, 89·20·5, 109·7·4, 125·14·6`; compact lefts
`17, 34, 53, 71, 94, 114, 131`.
- **Burger** (handheld only): `<button aria-label="Open menu" aria-expanded="false">` with a
`header/icon-menu.svg` Vector 22 tall × 30 wide, inset `-7.27% -5.33% 0 -5.33%`; `position:relative;
z-index:50`.
So desktop shows everything; laptop shows logo · En · nav · Log In · Buy NFT in one row; tablet shows
logo · Buy NFT · burger; mobile shows logo · burger.
### The menu (handheld) — the Featured-drop poster drops over the page
Appended to `<body>`: `position:fixed; inset:0; z-index:60`; while closed `pointer-events:none`,
`aria-hidden="true"` and `inert`.
- **Backdrop**: `position:absolute; inset:0; background:rgb(7 10 16 / .8)`, opacity `0 ↔ 1` with
`{ tension: 200, friction: 30 }`; click closes.
- **Sheet**: `position:absolute; left:12; right:12; top:12; bottom:12; display:flex;
flex-direction:column; overflow:hidden; background:var(--surface-poster); color:var(--on-inverse);
padding:20 24 32` (tablet: inset 24 all round, `padding:40 48 48`). Transform spring
`{ tension: 210, friction: 22 }`: closed `translateY(-115%) rotate(-7deg)` → open
`translateY(0%) rotate(-1.5deg)` — it drops in at a tilt and settles crooked, like a poster slapped up.
- Top row `display:flex; align-items:center; justify-content:space-between`: a `display:flex;
align-items:center; gap:12` group of a `drop/drop-crown-mobile.svg` Vector (23 × 29; tablet 32 × 40)
and `<span class="type-script" style="font-size:var(--type-eyebrow); color:var(--accent-strong)">menu</span>`
(tablet `font-size:2rem`); then the close `<button aria-label="Close menu">` with a
`questions/icon-close-compact.svg` Vector 22 square, inset `-9.38%`.
- Nav `<nav aria-label="Menu">`: `margin-top:40; display:flex; flex:1; flex-direction:column; gap:12`
(tablet `margin-top:64; gap:20`). The four links, `type-display`, `position:relative; width:fit-content;
font-size:3.5rem` (tablet `6rem`), colour `--on-inverse`, the current one (**Vault**) in
`--accent-strong` with a `drop/drop-scribble-mobile.svg` Vector under it (5 tall, full width, inset
`-42.35% -1.59%`, `position:absolute; bottom:-2; left:0; rotate:-2deg`). Clicking a link closes.
- Bottom block `display:flex; flex-direction:column; gap:24`: a `type-display` row
(`display:flex; align-items:center; gap:32; font-size:1.5rem`; tablet `gap:48; font-size:2.25rem`) of
"En" (`<button>`, `rgb(5 7 11 / .6)`) and "Log In" + `header/icon-user.svg` (20 square, tablet 28;
gap 8); then a **Buy NFT** PlateButton `md`, plate width 232, compact drip lefts
`26, 54, 86, 116, 152, 187, 215`.
- A barbed-wire arc in the corner: `position:absolute; right:-96; bottom:-112; width:360; height:360;
rotate:200deg; pointer-events:none; filter:drop-shadow(var(--elevation-wire))`,
`drop/drop-barbed-wire-arc.png` `object-fit:contain`, `fetchpriority="low"`.
- **Trail**: the five rows (four links, then the bottom block) spring `opacity 0 → 1`,
`translateX(-12%) → translateX(0%)` with `{ tension: 260, friction: 24 }`, delay 180 ms when opening (0
when closing), each row following the one before it (react-spring `useTrail`: each spring chases the
previous one's value — a soft cascade).
- Burger toggles; Escape closes; while open, scroll is stopped (Lenis + html lock) and restarted on close.
## Layout & sections (in order)
### 01 Hero — `<section>` (full viewport)
`position:relative; z-index:10; display:flex; flex-direction:column; justify-content:flex-end;
height:100svh`; `min-height` desktop 450, compact 480; padding laptop `0 32 48`, tablet `0 40 40`,
mobile `0 20 32`. Media and fades are pinned to the bottom edge with each frame's offsets, so the
composition holds as the window gets taller or shorter than the frame.
1. **Desktop left card** (desktop only) — `Rotated` box `position:absolute; bottom:-446; left:86;
height:470; width:409`, rotate `-19.7deg`, parallax −14. CardImage `intro/intro-card-tucked-overlay.png`
(alt "Prime Ape card"), 395 × 293 (h × w), `border-radius:var(--corner-art-tilt)`; motion card index 0.
It sits under the photo, peeking out below the hero into the next section.
2. **Photo frame** — `position:absolute; left:0; right:0; top:0; overflow:hidden`; `bottom` desktop 0,
laptop 119, tablet 104, mobile 242. Inside, a parallax layer (`position:absolute; inset:0`, distance 24,
no tilt — the vault recedes behind the copy) holding the photo slot (motion **photo**):
desktop `position:absolute; left:0; right:0; top:50%; translate:0 -50%; aspect-ratio:2752/1536`;
compact `position:absolute; inset:0`. The image `hero/hero-vault.png` (`alt=""`, `object-fit:cover`,
`fetchpriority="high"`, eager; preload it with `<link rel="preload" as="image">`). On compact the 16:9
photo is cropped to a taller box, so it renders wider than the viewport. Over it (desktop only) the
vignette `position:absolute; inset:0; background-image:var(--hero-vignette)`.
3. **Compact card** (compact only) — an anchor `position:absolute; left:0; right:0; bottom:0; height:0;
margin-inline:auto` (mobile column `max-width:24.375rem`, so it stays under the card row on wide phones)
holding a `Rotated` box `position:absolute`, rotate `-21deg`, parallax −8:
laptop `bottom:-387; left:28; height:482; width:423`; tablet `bottom:-330; left:12; height:370;
width:325`; mobile `bottom:-187; left:-5; height:208; width:182`. CardImage
`intro/intro-card-tucked-overlay.png`: laptop 401 × 299, tablet 308 × 230, mobile 173 × 129,
radius `var(--corner-art-tilt)`.
4. **Hero fade** `aria-hidden; pointer-events:none; position:absolute; left:0; right:0`: desktop
`bottom:-154; height:573; background-image:var(--hero-fade)`; compact `var(--hero-fade-compact)` at
laptop `bottom:118; height:325`, tablet `bottom:103; height:460`, mobile `bottom:241; height:301`.
5. **Card fade** (compact only) `position:absolute; left:0; right:0; background-image:var(--card-fade)`:
laptop `bottom:-146; height:281`; tablet `bottom:-145; height:225`; mobile `bottom:-83; height:144`.
6. **Copy** — `position:relative; display:flex`; desktop `position:absolute; left:0; right:0; bottom:0;
display:block; height:338` (all desktop coordinates below are inside this 1400 × 338 band); laptop
`align-items:flex-end; gap:48`; tablet `align-items:flex-end; gap:32`; mobile `flex-direction:column;
align-items:center; gap:20; width:100%; max-width:350; margin-inline:auto`.
- **Left column** `display:flex; flex-direction:column`; compact `flex:1`; laptop/tablet gap 12;
mobile `width:100%; align-items:center; gap:10`.
- Eyebrow "**card custody · zug**": crown box desktop `top:20; left:28; height:36; width:42`; label
box desktop `top:0; left:88; height:48; width:268`.
- `<h1 class="type-display" style="font-size:var(--type-display)">` (mobile `align-items:center`):
line 1 "**Rare cards**" (desktop `top:54; left:32`; mobile `text-align:center`); line 2 "**behind**"
(desktop `top:183; left:32`); mobile row `justify-content:center`. Holder: laptop 98 × 208 (h × w),
tablet 73 × 156, mobile 49 × 107 (desktop: static — the mark and scribble use band coordinates).
- Mark "**steel**" (`font-size:var(--type-script-lg)`), box: desktop `top:152; left:446;
height:175; width:325`; laptop `top:-7; left:2; 112 × 208`; tablet `top:-5; left:2; 83 × 155`;
mobile `top:-4; left:2; 56 × 104`.
- Scribble `hero/hero-scribble.svg` (laptop/tablet/mobile `hero/hero-scribble-laptop.svg`,
`-tablet.svg`, `-mobile.svg`), width desktop 290, laptop 186, tablet 138, mobile 93; inset desktop
`-74.12% -1.21%`, laptop `-51.88% -1.32%`, tablet `-42.35% -1.45%`, mobile `-42.34% -2.15%`. Box:
desktop `top:303; left:452; 20 × 290`; laptop `top:78; left:6; 14 × 186`; tablet `top:58; left:6;
12 × 138`; mobile `top:39; left:6; 10 × 93`.
- **Right column** `display:flex; flex-direction:column; align-items:flex-start; flex-shrink:0`;
desktop `position:absolute; top:172; left:933; width:435; gap:32`; laptop `width:340; gap:24`; tablet
`width:244; gap:24; padding-bottom:10`; mobile `width:100%; align-items:center; gap:20`.
- Lead `<p>` (motion **copy**): `font-size:var(--type-body); line-height:var(--line-copy);
color:var(--foreground-muted)`; mobile `width:332; text-align:center`. Copy: "**Rare cards kept in a
Swiss strongroom — every shelf, seal and signature published on**" + " " + "**a public ledger.**"
— on mobile only, a full-width break element sits between the two parts, so "a public ledger."
starts its own line.
- PlateButton `md` "**Request access**" → `#join`. Plate width desktop 256, laptop 232, tablet 219,
mobile 207. Desktop drips (left · h · w): `27·22·7, 58·12·5, 94·38·8, 127·16·5, 168·28·7,
207·10·5, 237·20·8`. Lefts: laptop `25, 52, 85, 115, 152, 187, 215`; tablet `24, 50, 80, 109, 144,
177, 203`; mobile `23, 47, 76, 103, 136, 167, 192`.
### 02 Cards + statement — `<section>`
Desktop `position:relative; height:1211` (Figma y 850…2061 of the 1400 frame). Compact `display:flex;
flex-direction:column`: laptop `gap:48; padding:0 32 48`; tablet `gap:40; padding:8 40 40`; mobile
`gap:32; padding:8 20 32` + mobile column.
- **Cards holder**: desktop static; compact `position:relative`, height laptop 482, tablet 370,
mobile 207.
- **Tucked card** — `Rotated` box `position:absolute` (compact `z-index:20`), rotate desktop
`1.45deg`, compact `1.5deg`, parallax 6. Box: desktop `top:126; left:547; 338 × 256`; laptop
`top:86; left:426; 292 × 220`; tablet `top:67; left:302; 223 × 170`; mobile `top:37; left:154;
125 × 95`. CardImage `intro/intro-card-tucked.png` (alt "Nype Ape card"), desktop 332 × 248, laptop
286 × 213, tablet 219 × 164, mobile 123 × 92, no radius, crop `top:-0.02%; left:0; height:100.04%;
width:100%`; card index 1.
- **Front card** — `Rotated` box `position:absolute; z-index:20`, rotate desktop `20.7deg`, compact
`20deg`, parallax −12. Box: desktop `top:80; left:632; 540 × 476`; laptop `top:33; left:523;
459 × 405`; tablet `top:25; left:377; 352 × 311`; mobile `top:13; left:193; 197 × 174`. CardImage
`intro/intro-card-front.png` (alt "Prime Ape KR777 card"): desktop 449 × 339 radius
`var(--corner-card)` (20); laptop 382 × 292 radius 17; tablet 293 × 224 radius 13; mobile 164 × 125
radius 7; card index 2.
- **Crown** (desktop only): `Rotated` box `position:absolute; top:560; left:1300; 50 × 57`, rotate
`-12deg`, `shared/crown-lg.svg` Vector 40 × 50, inset `-4.05% -3.26%`.
- **Text**: a wrapper — laptop `display:flex; align-items:flex-start; gap:48`; handheld
`display:flex; flex-direction:column`, gap tablet 24, mobile 20 — holding two columns (on handheld
both columns are `display:contents` and their children are re-ordered with `order`):
- **Left** `display:flex; flex-direction:column; align-items:flex-start`; desktop `position:absolute;
top:589; left:32; width:860; gap:40`; laptop `flex:1; gap:48`.
- Statement `<p class="type-display">` (motion **copy**): `font-size:var(--type-statement);
line-height:var(--line-statement); white-space:normal; color:var(--foreground)`; desktop
`text-box: trim-both cap alphabetic`; compact `width:100%`; handheld `order:1`. Copy: "**Each card
is bought outright, graded, sealed and shelved in Zug. The chain keeps the paperwork — ownership,
votes and audit trails — **" then `<span style="color:var(--accent)">`"**and nothing else.**"`</span>`.
- PlateButton `md` "**See the process**" → `#process`; handheld `order:4`, tablet `margin-top:16`,
mobile `margin-top:12`. Plate width desktop 260, laptop 235, tablet 223, mobile 210. Desktop drips
`28·22·7, 59·12·5, 95·38·8, 129·16·5, 171·28·7, 210·10·5, 241·20·8`; laptop lefts
`25, 53, 86, 116, 154, 189, 218`; tablet `24, 51, 82, 111, 146, 180, 207`; mobile
`23, 48, 77, 105, 138, 170, 195`.
- **Right** `display:flex; flex-direction:column`; desktop `position:absolute; top:737; left:984;
width:384; align-items:flex-end; gap:36`; laptop `width:340; flex-shrink:0; gap:24`.
- Aside `<p>` (motion **copy**, handheld `order:2`): body type, `--foreground-muted`. Copy: "**A rare
card can sit in a shoebox, a bank locker or a stranger’s listing. None of them can tell you where it
is tonight, who handled it last, or what it is insured for.**"
- Tags `<ul>` `display:flex; flex-wrap:wrap` (handheld `order:3`); desktop `width:356;
justify-content:flex-end; gap:4`; compact `width:100%; gap:8`. Each `<li class="type-caps">`
(motion **chip**, index 0…3): `border-radius:6; background:var(--surface); padding-inline:12;
font-size:var(--type-meta); letter-spacing:var(--letter-nav); color:var(--foreground);
outline:1.5px solid var(--border-strong); outline-offset:-1.5px`; desktop `padding-block:6;
line-height:var(--line-copy)`; compact `padding:8.5 13.5`. Tags: **Physical cards · Insured
storage · Open ledger · Member votes**.
### Barbed wire (twice)
A strand straddling two sections. `<div aria-hidden style="pointer-events:none; position:relative;
z-index:20">` with height desktop 0 (a zero-height layer between the sections), laptop 225, tablet 169,
mobile `22.051vw` (its own band on compact, not clipped — `<main>` clips the horizontal overflow).
Inside, a `Rotated` box (parallax given below) whose content is rotated **`transform: rotate(-88.17deg)
scaleY(-1)`**, holding the un-rotated strand `<div style="position:relative">` sized desktop
1618 × 499 (h × w), laptop 1184 × 365, tablet 888 × 274, mobile per instance (in `vw`, exact at 390),
holding `shared/barbed-wire-strand.png` (`alt=""`, `object-fit:fill`, lazy).
| | desktop box (top · left · h · w) | laptop | tablet | mobile box | mobile strand (h × w) | parallax |
|---|---|---|---|---|---|---|
| wire 1 (after Cards) | −207 · −153 · 550 · 1633 | −17 · −93 · 402 · 1195 | −23 · −64 · 302 · 896 | `-0.815vw · -23.295vw · 44.536vw · 132.21vw` | `130.99vw × 40.385vw` | 10 |
| wire 2 (after Questions) | −157 · −153 · 550 · 1633 | −24 · −96 · 402 · 1195 | −14 · −73 · 302 · 896 | `2.051vw · -8.328vw · 39.297vw · 116.655vw` | `115.578vw × 35.633vw` | −8 |
### 03 Process — `<section id="process">`
`position:relative; overflow:clip`; desktop `height:1036; background:var(--background)`. Compact
`display:flex; flex-direction:column`: laptop `gap:64; padding:48 32 72`; tablet `gap:48; padding:40 40 60`;
mobile `gap:40; padding:32 20 48` + mobile column.
- Ghost "**process**" (`--type-ghost-sm`), rotate `7deg`: desktop `top:56; left:603; 419 × 1026`; laptop
`top:10; left:206; 335 × 820`; tablet `top:10; left:114; 266 × 649`; hidden on mobile.
- Head `position:relative; display:flex; flex-direction:column` (desktop static block); laptop/tablet gap
12; mobile `align-items:center; gap:10`.
- Eyebrow "**the process**": crown desktop `top:108; left:80; 36 × 42`; label `top:93; left:140; 43 × 168`.
- `<h2>` display: line 1 "**From the table**" (desktop `top:166; left:84`; mobile centred); line 2 "**to
the**" (desktop `top:295; left:84`).
- **Struck word** holder (laptop 96 × 310, tablet 72 × 233, mobile 48 × 155): `<s>` (motion
**headline**, index 1) "**shoebox**", `position:absolute; top:0; left:0; color:rgb(242 244 248 / .28);
text-decoration:none` (desktop `top:295; left:491`), struck through by two scribbles:
- Scribble 1 `process/process-scribble-1.svg` (+ `-1-laptop`, `-1-tablet`, `-1-mobile`), rotate
`2deg`, width 498 / 320 / 243 / 165, inset desktop `-95.3% -0.9%`, laptop `-60.99% -0.9%`, tablet
`-45.74% -0.89%`, mobile `-42.35% -1.21%`. Box: desktop `top:357; left:481; 22 × 498`; laptop
`top:40; left:-6; 16 × 320`; tablet `top:30; left:-6; 13 × 243`; mobile `top:20; left:-6; 10 × 165`.
- Scribble 2 `process/process-scribble-2.svg` (+ `-2-laptop`, `-2-tablet`, `-2-mobile`), rotate
`-1deg`, width 484 / 306 / 229 / 151, inset desktop `-63.54% -0.62%`, laptop `-40.66% -0.63%`,
tablet `-31.77% -0.66%`, mobile `-31.76% -0.99%`. Box: desktop `top:371; left:487; 13 × 484`;
laptop `top:48; left:0; 10 × 306`; tablet `top:36; left:0; 9 × 229`; mobile `top:24; left:0; 7 × 151`.
- **Mark** holder (laptop 96 × 236, tablet 72 × 179, mobile 48 × 121): "**VAULT**"
(`--type-script-md`, rotate `-5deg`, no scribble). Box: desktop `top:240; left:980; 181 × 371`;
laptop `top:-10; left:2; 116 × 238`; tablet `top:-8; left:2; 87 × 179`; mobile `top:-5; left:2;
58 × 119`.
- **Steps** `<ol>`: compact `display:flex; flex-wrap:wrap`, gap laptop/tablet 16, mobile 12 (two per row
on laptop/tablet, one on mobile); desktop each `<li>`'s `Rotated` box is absolute:
| step | desktop box (top · left · h · w) | rotate |
|---|---|---|
| 01 | 520 · 76 · 411 · 311 | 2.2° |
| 02 | 536 · 391 · 407 · 306 | −1.4° |
| 03 | 523 · 704 · 406 · 304 | 1.2° |
| 04 | 534 · 1012 · 412 · 312 | −2.4° |
Compact: the box is `position:relative`, upright.
**Step card** `<article>` (motion **step**, index 0…3): `position:relative; overflow:clip;
border-radius:12; border:2px solid var(--border-strong); background:var(--surface)`; desktop
`height:400; width:296` with absolute layers; compact `display:flex; flex-direction:column; padding:24`,
width laptop 472, tablet 336, mobile 350, gap laptop/tablet 12, mobile 10.
- Top row `display:flex; align-items:flex-start; justify-content:space-between` (desktop block):
the number `<p aria-hidden class="type-display" style="font-size:var(--type-step-number);
color:var(--accent)">` (desktop `position:absolute; top:2; left:18`), and a crown `Rotated` box
(desktop `position:absolute; height:37; width:43`): odd steps (01, 03) `top:28; left:225`, rotate
`10deg`, `process/process-crown-a.svg`; even steps (02, 04) `top:21; left:230`, rotate `-10deg`,
`process/process-crown-b.svg`; compact upright, `process/process-crown-compact.svg`. Vector desktop
31 × 38, compact `1.73125rem × 2.15625rem`, inset `-4.05% -3.26%`.
- Title `<h3 class="type-display" style="font-size:var(--type-card-title)">` (desktop
`position:absolute; top:140; left:22`).
- Text `<p>`: `font-size:var(--type-body-sm); line-height:var(--line-copy);
color:var(--foreground-subtle)` (desktop `position:absolute; top:198; left:22; width:248`).
- Stats `<p class="type-caps">`: `border-radius:8; border:1px solid rgb(242 244 248 / .22);
background:rgb(242 244 248 / .03); font-size:var(--type-meta); letter-spacing:var(--letter-nav);
white-space:pre; color:var(--foreground)`; desktop `position:absolute; top:324; left:14; height:54;
width:264; padding:15 0 0 15`; compact `padding:12 14; white-space:pre-wrap`. Content:
`${label} ` + `<span style="color:var(--accent)">${value}</span>`, pairs separated by three spaces.
| # | title | text | stats |
|---|---|---|---|
| 01 | **Acquisition** | Bought at auction or from private collections. Two custodians sign every purchase. | Signatures **2** · Lead **3–10 d** |
| 02 | **Authentication** | Every card is checked by an independent grader before it gets a shelf number. | Graders **2** · Fakes kept **0** |
| 03 | **Custody** | Sealed, shelved and insured at full value in the Zug strongroom. | Insured **100%** · Keys **2** |
| 04 | **Registry** | Shelf, seal and signatures go on the public ledger the same day. | Published **same day** · Edits **0** |
### 04 Vault — `<section id="vault">`
`position:relative; overflow:clip`; desktop `height:1260; background:var(--background)`. Compact
`display:flex; flex-direction:column; align-items:center`: laptop `gap:48; padding:48 32 38`; tablet
`gap:40; padding:40 40 32`; mobile `gap:32; padding:32 20 26` + mobile column.
- Ghost "**vault**" (`--type-ghost-md`), rotate desktop `6deg`, compact `7deg`: desktop `top:86; left:666;
393 × 647`; laptop `top:10; left:256; 317 × 513`; tablet `top:10; left:186; 244 × 396`; hidden on mobile.
- Head `display:flex; width:100%; flex-direction:column; align-items:center`; gap 12 / 12 / 10.
- Eyebrow "**the vault**": crown desktop `top:148; left:80; 36 × 42`; label `top:136; left:140; 40 × 118`.
- `<h2>` — one line here, so compact is a single centred wrapping row: `display:flex; width:100%;
flex-wrap:wrap; align-items:center; justify-content:center; column-gap: 17 / 13 / 9`. Line "**Currently**"
(headline index 0; desktop `top:206; left:84`). Holder: laptop 106 × 168, tablet 79 × 127, mobile 53 × 87.
- Mark "**held**" (`--type-script-xl`). Box: desktop `top:166; left:743; 188 × 270`; laptop
`top:-6; left:2; 117 × 169`; tablet `top:-4; left:2; 87 × 126`; mobile `top:-3; left:2; 59 × 84`.
- Scribble `vault/vault-scribble.svg` (+ `-laptop`, `-tablet`, `-mobile`), width 246 / 149 / 111 / 75,
inset desktop `-74.12% -1.42%`, laptop `-56.12% -1.78%`, tablet `-42.35% -1.8%`, mobile
`-42.34% -2.67%`. Box: desktop `top:319; left:747; 18 × 246`; laptop `top:88; left:6; 13 × 149`;
tablet `top:65; left:6; 11 × 111`; mobile `top:44; left:6; 9 × 75`.
- **Lead in brackets** (compact width laptop/tablet 460, mobile 100 %): `[` desktop `top:348; left:930`;
`<p>` (motion **copy**) `font-size:var(--type-caption); line-height:var(--line-copy)`, desktop
`position:absolute; top:356; left:956; width:360; color:var(--foreground-soft)`, compact
`--foreground-muted`: "**A live slice of the registry. Every entry is a physical card on a numbered
shelf — graded, sealed, insured at full value.**"; `]` desktop `top:395; left:1328`.
- **Card fan** — a holder `position:relative; width:100%` (desktop static; height laptop/tablet 385,
mobile 228) of five `Rotated` boxes (absolute) with CardImages (motion card index = position 0…4).
Figma layer order: far left, far right, left, right, centre. The faded outer cards drift down
(recede), the centre card rises fastest, and the parallax tilts mirror left/right, so the fan opens as
the page scrolls. Mobile drops the two outer cards.
| card | image (alt) | box desktop (top · left · h · w) | laptop | tablet | mobile | rotate | card size d / l / t / m (h × w, radius) | opacity | crop | parallax · tilt |
|---|---|---|---|---|---|---|---|---|---|---|
| far left | `vault/vault-card-1.png` (Nype Ape card) | 636 · 103 · 369 · 314 | 66 · −39 · 340 · 285 | 64 · −102 · 333 · 280 | hidden | d −16°, c −14° | 316×236 r13 / 295×220 r12 / 289×216 r12 / — | .5 | — | 10 · −3 |
| far right | `vault/vault-card-2.png` (Preppy Ape card) | 636 · 983 · 369 · 314 | 66 · 698 · 340 · 285 | 64 · 510 · 333 · 280 | hidden | d 16°, c 14° | same as far left | .5 | `top:-0.01%; left:-0.3%; h:101.43%; w:101.66%` | 7 · 3 |
| left | `vault/vault-card-3.png` (Nype Ape card) | 568 · 305 · 396 · 318 | 34 · 156 · 320 · 254 | 32 · 64 · 313 · 250 | 28 · −3 · 188 · 151 | d −8°, l/t −7°, m −8° | 362×270 r15 / 295×220 r12 / 289×216 r12 / 172×128 r7 | .82 (mobile .75) | `top:0; left:-0.17%; h:100.75%; w:101.21%` | −4 · −2.5 |
| right | `vault/vault-card-4.png` (Hypebeast Ape KR999 card) | 568 · 777 · 396 · 318 | 34 · 534 · 320 · 254 | 32 · 374 · 313 · 250 | 28 · 203 · 188 · 151 | d 8°, l/t 7°, m 8° | as "left" | .82 (mobile .75) | `top:0.12%; left:5.15%; h:99.95%; w:89.71%` | −7 · 2.5 |
| centre | `vault/vault-card-5.png` (Red Hype Ape KR888 card) | 537 · 542 · 418 · 317 | 15 · 358 · 301 · 228 | 11 · 232 · 295 · 224 | 6 · 109 · 175 · 132 | 1.5° | 410×306 r17 / 295×220 r12 / 289×216 r12 / 172×128 r7 | 1 | — | −12 · 1 |
The card's own opacity is on the card box, so it multiplies with the reveal's opacity on the wrapper.
- **Crowns** (desktop only): `vault/vault-crown.svg` — box `top:536; left:85; 59 × 67`, rotate `14deg`,
Vector 46 × 58, inset `-4.06% -3.26%`; `shared/crown-lg.svg` — box `top:500; left:1248; 50 × 57`, rotate
`-12deg`, Vector 40 × 50, inset `-4.05% -3.26%`.
- **Stats plate** `<p class="type-caps">`: `display:flex; align-items:center; justify-content:center;
border-radius:var(--corner-plate); border:2px solid var(--border-strong); background:var(--surface);
font-size:var(--type-plate); letter-spacing:var(--letter-nav); color:var(--foreground)`; desktop
`position:absolute; top:1026; left:401; height:60; width:600; justify-content:flex-start;
padding-left:46; white-space:pre`; laptop/tablet `gap:26; padding:14 40`; mobile `width:100%;
flex-wrap:wrap; column-gap:14; row-gap:6; padding:14 18`. Content: **Shelf** `A-04` · **Red Hype Ape**
`KR888` · **BGS** `9.5` · **Value** `$96,000` — each `${label} ` + accent value; desktop separates the
pairs with four literal spaces (a desktop-only span), compact uses the gap.
- **CTA**: a row `display:flex; width:100%; justify-content:center` (compact `padding-bottom:34`) with a
`Rotated` box rotated `2deg` — desktop `position:absolute; top:1144; left:554; 75 × 290`; compact
`position:relative`, laptop 71 × 263, tablet 67 × 249, mobile 66 × 235 — holding PlateButton `md`
"**View the registry**" → `#`. Plate width desktop 281, laptop 255, tablet 241, mobile 227. Desktop drips
`29·22·7, 63·12·5, 103·38·8, 139·16·5, 184·28·7, 226·10·5, 260·20·8`; laptop lefts
`26, 57, 93, 126, 167, 205, 236`; tablet `25, 54, 88, 119, 158, 194, 223`; mobile
`24, 51, 83, 113, 149, 183, 210`.
### 05 Featured drop — `<section>` (the light paper poster)
`position:relative`; desktop `height:1130; overflow:clip; background:var(--background)`. Compact
`display:flex; flex-direction:column; align-items:center`: laptop `padding:118 32`; tablet `padding:92 40`;
mobile `padding:64 30` + mobile column.
- **Poster** `position:relative; overflow:clip; background:var(--surface-poster)` (`#e6e7ea`):
desktop `position:absolute; top:170; left:270; height:840; width:860`; laptop 781 × 800; tablet
586 × 600 (all with absolute children, coordinates below are poster-relative); mobile `display:flex;
width:100%; flex-direction:column; gap:32; padding:22` (auto-layout).
- Header (mobile `display:flex; width:100%; flex-direction:column; align-items:center; gap:10`), its
eyebrow row mobile `display:flex; align-items:center; gap:12`:
- Crown `Rotated` (absolute on desktop/laptop/tablet), rotate desktop `8deg`: desktop `top:52;
left:56; 32 × 38`; laptop `top:50; left:48`; tablet `top:38; left:36`. Vector `drop/drop-crown.svg`
(`-laptop`, `-tablet`, `-mobile`), desktop 28 × 34, laptop `1.73125rem × 2.15625rem`, tablet
`1.6375rem × 2.0375rem`, mobile `1.45rem × 1.8rem`; inset `-4.06% -3.26%` (mobile `-4.31% -3.47%`).
- Label `Rotated`, rotate desktop `-3deg`: desktop `top:39; left:108; 39 × 172`; laptop `top:48;
left:96`; tablet `top:36; left:82`. `<p class="type-script" style="font-size:var(--type-eyebrow-sm);
color:var(--accent-strong)">featured drop</p>`.
- `<h2 class="type-display" style="font-size:var(--type-poster-title)">`, colour desktop
`--on-poster`, compact `--on-inverse`; mobile `display:flex; width:100%; flex-wrap:wrap;
align-items:center; justify-content:center; column-gap:9`.
- "**Drop I**" (headline index 0): desktop `top:96; left:58`; laptop `top:90; left:46`; tablet
`top:76; left:34`.
- Mobile holder `position:relative; height:48; width:143; flex-shrink:0`:
- Mark "**KR777**" (`--type-script-sm`, `--accent-strong`), box: desktop `top:62; left:390;
153 × 372`; laptop `top:69; left:287; 115 × 279`; tablet `top:60; left:215; 86 × 210`; mobile
`top:-5; left:2; 57 × 140`.
- Scribble `drop/drop-scribble.svg` (+ `-laptop`, `-tablet`, `-mobile`), width 335 / 251 / 189 /
126, inset desktop `-74.12% -1.04%`, laptop `-50.83% -0.96%`, tablet `-42.35% -1.06%`, mobile
`-42.35% -1.59%`. Box: desktop `top:190; left:395; 22 × 335`; laptop `top:161; left:291;
18 × 251`; tablet `top:129; left:219; 15 × 189`; mobile `top:37; left:6; 11 × 126`.
- **Card** (mobile holder `position:relative; margin-inline:auto; height:319; width:250`): `Rotated`
box `position:absolute`, rotate desktop `4.54deg`, compact `4deg`, parallax −10. Box: desktop
`top:275; left:389; 566 × 437`; laptop `top:222; left:355; 529 × 410`; tablet `top:170; left:265;
397 × 308`; mobile `top:5; left:5; 310 × 240`. CardImage `drop/drop-card.png` (alt "Nype Ape KR777
card"): desktop 536 × 396 radius 22; laptop 504 × 376 radius 21; tablet 378 × 282 radius 16; mobile
295 × 220 radius 12; compact `box-shadow:var(--elevation-card)`; crop `top:-2.24%; left:-4.55%;
height:110.45%; width:114.14%`.
- **Points** `<ul>` (mobile `display:flex; width:100%; flex-direction:column; gap:20`; each `<li>`
mobile `display:flex; align-items:flex-start; gap:12`):
- Bullet `Rotated` (absolute desktop/laptop/tablet), rotate desktop `90deg`; `drop/drop-bullet.svg`,
laptop+tablet `drop/drop-bullet-compact.svg`, mobile `drop/drop-bullet-mobile.svg`; Vector desktop
8 × 9 (h × w), laptop/tablet 11 × 9, mobile 22 × 10; inset desktop `0 6.7% 25% 6.7%`,
laptop/tablet `-4.55% -5.56%`, mobile `0 0 0 -5%`. Box 9 × 8 on desktop. Point 1: desktop `top:315;
left:56`, laptop `top:339; left:48`, tablet `top:216; left:36`. Point 2: desktop `top:413; left:56`,
laptop `top:476; left:48`, tablet `top:362; left:36`.
- Text `<p>` (motion **copy**, index 0 / 1): `font-size:var(--type-poster-copy);
line-height:var(--line-poster)`; colour desktop `rgb(7 9 13 / .85)`, laptop/tablet
`rgb(5 7 11 / .88)`, mobile `rgb(5 7 11 / .85)` (mobile `flex:1`). Point 1: desktop `top:308;
left:85; width:290`; laptop `top:330; left:70; width:263`; tablet `top:208; left:58; width:192`.
Point 2: desktop `top:406; left:85`; laptop `top:467; left:70`; tablet `top:354; left:58` (same
widths). Copy: "**Graded BGS 9.5, sealed and shelved in Zug on 12.03.26. Two custodians signed the
intake.**" / "**Holders vote on loans, shows and resale. Every vote lands on the public ledger the
day it closes.**"
- **Shelf + price** `<p class="type-caps">`: `font-size:var(--type-poster-meta);
letter-spacing:var(--letter-nav); white-space:pre`; desktop `line-height:var(--line-meta);
color:var(--on-poster); top:752; left:64`; laptop `top:713; left:88`; tablet `top:531; left:66`;
compact `--on-inverse`. Content: "**Shelf A-11 · **" + `<span style="color:var(--accent-strong)">`
"**$58,400**".
- **Barbed-wire arcs** around the poster (section-relative, `pointer-events:none`): each a `Rotated` box
(square) with its own parallax, holding `<div style="position:relative;
filter:drop-shadow(var(--elevation-wire))">` (square, size given) with `drop/drop-barbed-wire-arc.png`
(`object-fit:contain`). The wire layers separate from the card on scroll. Mobile keeps the last two.
| arc | rotate | box desktop (top · left · size) | laptop | tablet | mobile | inner size d / l / t / m | parallax |
|---|---|---|---|---|---|---|---|
| 1 | 4° | 34 · 534 · 1131 | −8 · 358 · 1052 | −3 · 268 · 789 | hidden | 1060 / 986 / 740 / — | 8 |
| 2 | −176° | 14 · −266 · 1131 | −27 · −386 · 1052 | −17 · −290 · 789 | hidden | 1060 / 986 / 740 / — | −6 |
| 3 | −86° | −370 · 144 · 1153 | −385 · −6 · 1072 | −285 · −4 · 804 | −162 · −36 · 478 | 1080 / 1005 / 753 / 448 | 12 |
| 4 | 94° | 398 · 104 · 1153 | 329 · −43 · 1072 | 251 · −32 · 804 | 528 · −52 · 478 | 1080 / 1005 / 753 / 448 | −9 |
### 06 Record — `<section id="record">`
`position:relative; overflow:clip`; desktop `height:1416; background:var(--background)`. Compact
`display:flex; flex-direction:column`: laptop `gap:48; padding:38 32 72`; tablet `gap:40; padding:32 40 60`;
mobile `gap:32; padding:26 20 48` + mobile column.
- Ghost "**record**" (`--type-ghost-sm`), rotate `7deg`: desktop `top:66; left:624; 399 × 858`; laptop
`top:10; left:340; 319 × 687`; tablet `top:10; left:112; 253 × 544`; hidden on mobile.
- Head: laptop `align-items:flex-start; gap:12`; tablet `align-items:center; gap:12`; mobile
`align-items:center; gap:10`. h2 handheld `align-items:center`, line 1 handheld centred, row handheld
`justify-content:center`.
- Eyebrow "**on the record**": crown desktop `top:148; left:80; 36 × 42`; label `top:132; left:140; 44 × 190`.
- Line 1 "**Everything is**" (desktop `top:206; left:84`); line 2 "**on the**" (desktop `top:335;
left:84`). Holder laptop 98 × 277, tablet 73 × 208, mobile 49 × 142.
- Mark "**record**" (`--type-script-lg`). Box: desktop `top:298; left:494; 182 × 433`; laptop `top:-9;
left:2; 117 × 277`; tablet `top:-7; left:2; 87 × 207`; mobile `top:-5; left:2; 58 × 139`.
- Scribble `record/record-scribble.svg` (+ `-laptop`, `-tablet`, `-mobile`), width 389 / 249 / 186 / 125,
inset desktop `-74.13% -0.9%`, laptop `-51.89% -0.98%`, tablet `-42.35% -1.08%`, mobile
`-42.35% -1.6%`. Box: desktop `top:450; left:501; 25 × 389`; laptop `top:75; left:6; 18 × 249`; tablet
`top:56; left:6; 14 × 186`; mobile `top:38; left:6; 11 × 125`.
- **Stickers** — a holder `position:relative; width:100%` (desktop static; height laptop 245, tablet 234,
mobile 334) of four `Rotated` boxes (absolute) holding motion **sticker** elements (index 0…3) plus a
crown. Label style: `type-caps; position:absolute; font-size:var(--type-meta)`, letter-spacing desktop
`--letter-caps`, compact `--letter-address`. Value style: `type-display; position:absolute`. Label
offsets (all stickers unless noted): laptop `top:20; left:20`, tablet `top:16; left:16`, mobile
`top:14; left:14`; value offsets: laptop `top:42; left:18`, tablet `top:37; left:14`, mobile
`top:33; left:12`.
1. **Insured value · $48.6M** — paper on a blue offset. Box rotate `4deg`: desktop `top:620; left:78;
240 × 444`; laptop `top:35; left:-5; 177 × 315`; tablet `top:53; left:-4; 132 × 221`; mobile
`top:25; left:-3; 113 × 196`. Sticker `position:relative`: desktop 210 × 430, laptop 156 × 305,
tablet 117 × 213, mobile 100 × 190. Inside: an accent block `position:absolute` at desktop `top:10;
left:10; 200 × 420`, laptop `8/8; 148 × 297`, tablet `6/6; 111 × 207`, mobile `5/5; 95 × 185`; over it
the paper block `position:absolute; top:0; left:0; background:var(--surface-inverse)` desktop
200 × 420, laptop 148 × 297, tablet 111 × 207, mobile 95 × 185. Label "Insured value"
`rgb(5 7 11 / .7)` desktop `top:22; left:26`; value "$48.6M" `font-size:var(--type-stat);
color:var(--on-inverse)` desktop `top:50; left:22`.
2. **Cards vaulted · 12,480** — a solid blue sticker. Box rotate `-3deg`: desktop `top:585; left:544;
250 × 392`; laptop `top:25; left:316; 162 × 276`; tablet `top:45; left:201; 121 × 194`; mobile
`top:19; left:181; 104 × 172`. Sticker `position:relative; background:var(--accent)`: desktop
230 × 380, laptop 148 × 269, tablet 111 × 188, mobile 95 × 167. Label `rgb(5 7 11 / .75)` desktop
`top:24; left:26`; value `font-size:var(--type-stat-lg); color:var(--on-inverse)` desktop `top:60; left:20`.
3. **Audits a year · 4×** — a dark outlined sticker. Box rotate `3deg`: desktop `top:679; left:925;
222 × 251`; laptop `top:51; left:600; 157 × 175`; tablet `top:64; left:380; 118 × 147`; mobile
`top:188; left:-2; 102 × 142`. Sticker desktop 210 × 240, laptop 148 × 167, tablet 111 × 141, mobile
95 × 137; its ground `position:absolute; inset:0; border:2px solid var(--border-strong);
background:var(--surface)`, radius desktop `var(--corner-sticker)` (14), compact 12. Label
`--foreground-subtle` desktop `top:24; left:24`; value `font-size:var(--type-stat);
color:var(--accent)` desktop `top:54; left:22`.
4. **0 · lost since 2021** — a seal ring. Box (square) rotate desktop `-9deg`, laptop `9deg`, tablet
`-8deg`, mobile `9deg`: desktop `top:433; left:1077; 286`; laptop `top:-16; left:755; 235`; tablet
`top:9; left:503; 197`; mobile `top:121; left:127; 204`. Sticker square desktop 250, laptop 205,
tablet 174, mobile 178, holding: the outer ring Vector `record/record-ring-outer.svg` (+ `-laptop`,
`-tablet`, `-mobile`) `position:absolute; inset:0`; the inner ring Vector
`record/record-ring-inner.png` (+ `record-ring-inner-laptop.png`, `-tablet.png`, `-mobile.png`)
`position:absolute`, desktop `top:14; left:14; 222` square, laptop `11/11; 183`, tablet `10/10; 154`,
mobile `11/11; 156`; the value "**0**" `type-display; font-size:var(--type-stat-ring);
color:var(--foreground)` at desktop `top:28; left:88`, laptop `top:41; left:77`, tablet `top:42; left:68`,
mobile `top:50; left:73`; and the label "**lost since 2021**" `type-caps; position:absolute;
font-size:var(--type-fine); color:var(--accent)`, letter-spacing desktop `--letter-caps`, compact
`--letter-address`, at desktop `top:176; left:64`, laptop `top:144; left:41`, tablet `top:114;
left:28`, mobile `top:111; left:37`.
5. **Crown** `record/record-crown.svg` (+ `-laptop`, `-tablet`, `-mobile`), box rotate `12deg`: desktop
`top:560; left:511; 52 × 59`; laptop `top:-6; left:299; 44 × 50`; tablet `top:-8; left:187; 37 × 42`;
mobile `top:-6; left:171; 26 × 30`. Vector desktop 42 × 52, laptop 35 × 44, tablet 30 × 37, mobile
21 × 26; inset desktop `-4.05% -3.26%`, laptop `-4.06% -3.27%`, tablet `-4.05% -3.26%`, mobile
`-4.81% -3.88%`.
- **Ledger** `<ul>`: `width:100%; border-top:1px solid rgb(242 244 248 / .16)`; desktop
`position:absolute; top:986; left:84; width:1232`. Each `<li>` (motion **ledger**, index 0…3):
`position:relative; border-bottom:1px solid rgb(242 244 248 / .16)`; desktop `height:84` with absolute
cells; laptop/tablet `display:flex; align-items:center; gap:20; padding-block:20`; mobile
`display:grid; grid-template-columns:1fr auto; align-items:center; row-gap:10; padding-block:18`.
- Date `type-caps; font-size:var(--type-row-date); letter-spacing:var(--letter-nav);
color:var(--accent)` — desktop `position:absolute; top:29; left:0`; laptop `width:120; flex-shrink:0`;
tablet `width:100; flex-shrink:0`; mobile column 1, row 1.
- Title `type-caps; font-size:var(--type-row-title); letter-spacing:var(--letter-row);
color:var(--foreground)` — desktop `position:absolute; top:27; left:156`; compact
`line-height:var(--line-row); white-space:normal`; laptop/tablet `flex:1`; mobile spans both columns,
row 2.
- Ref `type-caps; font-size:var(--type-meta); letter-spacing:var(--letter-caps);
color:var(--foreground-subtle)` — desktop `position:absolute; top:31; right:186`; laptop
`flex-shrink:0`; hidden on tablet; mobile spans both columns, row 3.
- Stamp: a `Rotated` box — desktop `position:absolute; top:50%; left:1162; translate:-50% -50%`;
compact `flex-shrink:0`; mobile column 2, row 1, `justify-self:end` — holding
`<span class="type-display">` (motion **stamp**, index = row) `display:block; border-radius:6;
border:3px solid var(--accent); font-size:var(--type-stamp); color:var(--accent)`; desktop `height:44;
padding:6 11 0 13; opacity:.9 !important` (the stamp's opacity is pinned on desktop — only its
transform and blur animate there; the row's fade still hides it until the row reveals); compact
`padding:6 14`.
| row | date | title | ref | stamp | stamp rotate | compact box (laptop/tablet · mobile, h × w) |
|---|---|---|---|---|---|---|
| 1 | 12.03.26 | Quarterly vault audit closed — independent auditor | 0x9f2c | Closed | d 7°, c 6° | 50 × 98 · 47 × 92 |
| 2 | 28.02.26 | Insurance renewed — full replacement value | POL-4411 | Renewed | −4° | 48 × 112 · 45 × 105 |
| 3 | 14.01.26 | Intake lot sealed — 412 cards, two signatures | LOT-3311 | Sealed | 3° | 45 × 97 · 43 × 91 |
| 4 | 02.12.25 | Governance vote #18 closed — 91.4 % turnout | VOTE-018 | Closed | d −6°, c −5° | 48 × 97 · 46 × 92 |
### 07 Questions — `<section id="questions">`
`position:relative; overflow:clip`; desktop `height:816; background:var(--background)`. Laptop
`display:flex; align-items:flex-start; gap:48; padding:48 32`; handheld `display:flex;
flex-direction:column`; tablet `gap:40; padding:40`; mobile `gap:32; padding:32 20` + mobile column.
- Ghost "**ask**" (`--type-ghost-xl`), rotate `8deg`, desktop only: `top:296; left:7; 453 × 600`.
- **Left** `position:relative; display:flex; flex-direction:column` (desktop static block); laptop
`width:400; flex-shrink:0; gap:24`; tablet `width:100%; align-items:center; gap:24`; mobile
`width:100%; gap:20`. Its head group (desktop `display:contents`): laptop `align-items:flex-start;
gap:12`; tablet `align-items:center; gap:12`; mobile `align-items:center; gap:10`.
- Eyebrow "**your questions**": crown desktop `top:148; left:80; 36 × 42`; label `top:132; left:140;
44 × 202`.
- `<h2>` at `font-size:var(--type-display-sm)`: line 1 "**Ask us**" (desktop `top:206; left:84`;
bottom margin laptop −11, tablet −10, mobile −7; handheld centred); line 2 "**the hard**" (desktop
`top:309; left:84`); row column-gap laptop 14, tablet 13, mobile 9, handheld centred. Holder laptop
82 × 189, tablet 73 × 169, mobile 49 × 116.
- Mark "**stuff**" (`--type-script-xs`). Box: desktop `top:284; left:527; 141 × 280`; laptop `top:-6;
left:2; 95 × 188`; tablet `top:-6; left:2; 84 × 168`; mobile `top:-4; left:2; 57 × 113`.
- Scribble `questions/questions-scribble.svg` (+ `-laptop`, `-tablet`, `-mobile`), width 250 / 168 / 150 /
101, inset desktop `-74.12% -1.4%`, laptop `-43.41% -1.22%`, tablet `-42.35% -1.33%`, mobile
`-42.35% -1.98%`. Box: desktop `top:404; left:534; 18 × 250`; laptop `top:65; left:6; 14 × 168`;
tablet `top:58; left:6; 13 × 150`; mobile `top:39; left:6; 10 × 101`.
- **Note in brackets** (compact width laptop/tablet 400, mobile 100 %): `[` desktop `top:508; left:84`;
`<p>` (motion **copy**) `font-size:var(--type-body-sm); line-height:var(--line-copy);
color:var(--foreground-muted)`, desktop `position:absolute; top:514; left:112; width:300`: "**Still not
sure? Write to us and we’ll walk you through the vault, shelf by shelf.**"; `]` desktop `top:528; left:424`.
- **Accordion**: `border-top:1px solid rgb(242 244 248 / .2)`; desktop `position:absolute; top:206;
left:850; width:466`; laptop `flex:1`; handheld `width:100%`. Five native `<details name="faq">`
(one open at a time; the first is `open`), each `border-bottom:1px solid rgb(242 244 248 / .2)`:
- `<summary>` `display:flex; cursor:pointer; list-style:none; align-items:center;
justify-content:space-between` (hide the marker, incl. `::-webkit-details-marker`); desktop
`padding:23 2 30 0`; compact `gap:16; padding-block:22`; when open, `padding-bottom` desktop 16,
laptop/tablet 12, mobile 10. It holds the question `<span class="type-caps" style="flex:1;
font-size:var(--type-row-title); letter-spacing:var(--letter-row); color:var(--foreground)">` (compact
`line-height:var(--line-row); white-space:normal`) and an icon: closed → plus
(`questions/icon-plus.svg`, compact `questions/icon-plus-compact.svg`), open → close
(`questions/icon-close.svg`, compact `questions/icon-close-compact.svg`); Vector desktop 18 square,
inset `-8.33%`; compact 16 square, inset `-9.38%`.
- Answer `<p>` (motion **faq** — drops open every time its item opens): `font-size:var(--type-body-sm);
line-height:var(--line-copy); color:var(--foreground-subtle)`; desktop `width:400; padding-bottom:26`;
compact `padding-bottom:22`.
| question | answer |
|---|---|
| Where is my card physically? | On a numbered shelf in the Zug strongroom. The shelf, the seal number and both custodian signatures are on the public ledger. |
| Who can take it out of the vault? | Only both custodians, signing together, after a member vote or your redemption request. Every exit hits the ledger. |
| What does the chain actually do? | It keeps the paperwork: who owns each card, how members voted, every audit. The card itself never leaves the shelf. |
| What if a card is damaged or lost? | Every card is insured at full replacement value. Holders get the payout and the incident report goes on the ledger. |
| How do I get my card back? | Request redemption in your account. Once both custodians confirm ownership, the card ships insured from Zug. |
Then **barbed wire 2** (see Barbed wire).
### 08 Join + 09 Footer — one wrapper
`<div style="position:relative">`; desktop `height:1286; overflow:clip; background:var(--background)`
(Join is its upper 854, the footer its lower 432).
#### Join — `<section id="join">`
Desktop `position:absolute; left:0; right:0; top:0; height:854`. Compact `display:flex;
flex-direction:column; overflow:clip`: laptop `gap:48; padding:48 32 110`; tablet `gap:40; padding:40 40 120`;
mobile `gap:32; padding:32 20 38` + mobile column.
- Ghost "**join**" (`--type-ghost-lg`), rotate desktop `8deg`, compact `7deg`: desktop `top:96; left:710;
425 × 535`; laptop `top:10; left:617; 324 × 412`; tablet `top:10; left:441; 255 × 325`; hidden on mobile.
- Head: laptop/tablet `align-items:flex-start; gap:12`; mobile `align-items:center; gap:10`.
- Eyebrow "**membership**": crown desktop `top:148; left:80; 36 × 42`; label `top:134; left:140; 42 × 155`.
- Line 1 "**Put your cards**" (desktop `top:206; left:84`; mobile centred); line 2 "**behind the**"
(desktop `top:335; left:84`). Holder laptop 98 × 190, tablet 73 × 143, mobile 49 × 98.
- Mark "**door**" (`--type-script-lg`). Box: desktop `top:307; left:734; 173 × 297`; laptop `top:-6;
left:2; 111 × 190`; tablet `top:-5; left:2; 82 × 142`; mobile `top:-3; left:2; 55 × 95`.
- Scribble `join/join-scribble.svg` (+ `-laptop`, `-tablet`, `-mobile`), width 298 / 169 / 126 / 85, inset
desktop `-74.12% -1.17%`, laptop `-51.88% -1.45%`, tablet `-42.35% -1.59%`, mobile `-42.34% -2.35%`.
Box: desktop `top:454; left:741; 20 × 298`; laptop `top:79; left:6; 14 × 169`; tablet `top:59; left:6;
11 × 126`; mobile `top:40; left:6; 9 × 85`.
- **Card** (not on mobile): `Rotated` box `position:absolute`, rotate desktop `11deg`, compact `8deg`,
parallax −10: desktop `top:342; left:1057; 476 × 392`; laptop `top:207; left:660; 440 × 353`; tablet
`top:194; left:406; 419 × 337`. CardImage `join/join-card.png` (alt "Blue Leopard Nype Ape card"),
radius 17 (tablet 16): desktop 423 × 317, laptop 402 × 300, tablet 383 × 286; crop `top:0;
height:100.65%`, desktop `left:-0.3%; width:100.33%`, compact `left:-0.45%; width:100.64%`.
- **Crown** (not on mobile): box rotate desktop `-14deg`, compact `14deg`: desktop `top:253; left:1296;
63 × 72`; laptop `top:179; left:921; 52 × 59`; tablet `top:191; left:658; 52 × 59`. Vector
`join/join-crown.svg` (compact `join/join-crown-compact.svg`), desktop 49 × 61, compact 41 × 51; inset
desktop `-4.05% -3.26%`, compact `-4.18% -3.36%`.
- **Form** `<form action="#">` (submitting goes nowhere): `position:relative; display:flex;
align-items:flex-start`; desktop `position:absolute; top:598; left:84; display:block; height:65;
width:808`; laptop `gap:16`; tablet `flex-direction:column; gap:16`; mobile `width:100%;
flex-direction:column; gap:20`.
- `<label for="join-email" class="sr-only">Email</label>` and `<input id="join-email" type="email"
name="email" placeholder="you@domain.com">`: `border:2px solid var(--border-strong);
background:var(--surface); font-size:var(--type-body); line-height:1; color:var(--foreground)`,
placeholder `--foreground-subtle`; desktop `position:absolute; top:0; bottom:0; left:0; width:520;
padding:0 0 7 24`; compact `padding-inline:20`; laptop 56 × 360; tablet 52 × 340; mobile height 52,
full width.
- PlateButton `md` "**Enter the vault**" → `#`; desktop `position:absolute; top:0; left:540`. Plate width
desktop 261, laptop 236, tablet 340, mobile 350. Desktop drips `28·22·7, 59·12·5, 96·38·8, 130·16·5,
171·28·7, 211·10·5, 242·20·8`; laptop lefts `25, 53, 86, 117, 155, 190, 218`; tablet
`26, 64, 108, 156, 210, 264, 312`; mobile `27, 66, 111, 160, 216, 272, 321`.
- **Fine print** `<p class="type-caps">`: `font-size:var(--type-fine); color:var(--foreground-subtle)`;
desktop `position:absolute; top:688; left:84; letter-spacing:var(--letter-caps-wide);
white-space:pre`; compact `line-height:var(--line-meta); letter-spacing:var(--letter-caps);
white-space:pre-wrap`; laptop full width, tablet 340, mobile full width. Copy: "**Membership opens in
blocks of fifty **" + (mobile-only `<br>`) + "**· next block closes 30 April**".
#### Footer — `<footer>`
Desktop `position:absolute; left:0; right:0; top:854; bottom:0`. Compact `display:flex;
flex-direction:column; overflow:clip`: laptop `gap:48; padding:0 32 198`; tablet `gap:40; padding:0 40 150`;
mobile `gap:32; padding:0 20 83` + mobile column.
- Rule `height:1px`: desktop `position:absolute; top:0; left:84; width:1232; background:rgb(242 244 248 / .14)`;
compact `width:100%; background:rgb(242 244 248 / .18)`.
- Main row (laptop/tablet `display:flex; align-items:flex-start; justify-content:space-between`;
desktop and mobile `display:contents`):
- Brand block `display:flex; flex-direction:column` (desktop contents), gap 12 / 12 / 10:
`<p class="type-display" style="font-size:var(--type-footer-logo)">HELD-MINT</p>` (desktop
`position:absolute; top:30; left:84`) and `<address class="type-caps">` `font-size:var(--type-fine);
letter-spacing:var(--letter-address); color:var(--foreground-subtle); font-style:normal` (desktop
`top:82; left:84`; tablet `width:210; line-height:var(--line-meta); white-space:normal`):
"**Bahnhofstrasse 21, 6300 Zug, Switzerland**".
- Columns `display:flex; align-items:flex-start` (desktop contents), gap laptop 72, tablet 48, mobile
28. Three `<nav aria-label="…">` `display:flex; flex-direction:column` (gap 12 / 12 / 10); desktop
`position:absolute; top:34; display:block; left:760 / 960 / 1160`. Each: a title `<p class="type-caps"
style="font-size:var(--type-column); letter-spacing:var(--letter-column); color:var(--accent)">` and a
`<ul>` `display:flex; flex-direction:column` (desktop `margin-top:16; gap:10`; laptop/tablet gap 12;
mobile 10) of `<li style="width:fit-content">` **SprayLinks** (`href="#"`, `type-caps;
display:block; font-size:var(--type-footer-link); letter-spacing:var(--letter-row);
color:var(--foreground)`, hover `--accent`; underline box `top:0.9em; left:-0.9em; height:0.8em;
width:calc(100% + 1.6em)` at the link's font-size).
**Vault**: Registry · Custody · Insurance — **Protocol**: White paper · Governance · Audits —
**Company**: About · Careers · Contact.
- Legal row (laptop/tablet `display:flex; justify-content:space-between`; mobile `display:flex;
flex-direction:column; gap:10`; desktop contents): two `<p class="type-caps">`
`font-size:var(--type-legal); color:var(--foreground-subtle)`, letter-spacing desktop
`--letter-caps-wide`, compact `--letter-caps`: "**© 2026 HELD-MINT AG**" (desktop `position:absolute;
top:174; left:84`) and "**Terms · Privacy · Disclosures**" (desktop `top:174; right:84`).
- **Wordmark** `<p aria-hidden class="type-display">HELD-MINT</p>` (motion **wordmark** — the outline
letters stand up one by one): `position:absolute; font-size:var(--type-wordmark); color:transparent;
opacity:.7; -webkit-text-stroke:1px var(--foreground)`; desktop `top:212; left:40`; laptop `top:259;
left:30`; tablet `top:236; left:22`; mobile `top:312; left:11`. It bleeds off the bottom and is clipped
by the wrapper (desktop) / the footer (compact).
## Reduced motion
`prefers-reduced-motion: reduce` makes every spring immediate (the reveals, preloader cards, stamp and
lift jump to their end values; the curtain still covers the page until loading is done), and the
parallax layers rest at their designed place.
## Fixed parameters (bake these in)
Every constant is given in its section above; the ones that recur everywhere:
- **Breakpoints**: desktop `≥ 1200` (frame 1400), laptop `896–1199` (1024), tablet `700–895` (768), mobile
`< 700` (390); groups compact `< 1200`, handheld `< 896`. Root font-size `1.142857vw` / `1.5625vw` /
`2.083333vw` / `min(4.102564vw, 19.2px)`; mobile column `max-width:24.375rem`, centred. Preloader
layouts: wide, portrait `(orientation: portrait)`, tall `(max-aspect-ratio: 5/8)`.
- **Palette**: ground `#070a10`, surface `#0c1119`, paper `#f2f4f8`, poster `#e6e7ea`, ink `#05070b` /
`#07090d`, muted `#d5dae3`, soft `#c9d0db`, subtle `#8f98a8`, accent `#4a8cff`, accent-strong `#2a63f0`,
border `#ffffff`.
- **Springs**: PUNCH `{420, 22}` · SOFT `{120, 26}` · HEAVY `{60, 30}` · DRIP `{140, 13}` · mark spray
`{55, 20}` (delay 300) · scribble spray `{70, 20}` (delay 700) · copy lines `{170, 26}` (stagger 70,
slot index × 120) · sticker `{420, 18}` · stamp chain `{260, 26}` → `{900, 30}` → `{400, 18}` (delay
200, stagger 160) · wordmark letters `{200, 22}` (stagger 45, y 80 px) · parallax `{280, 40}` · header
backdrop `{220, 30}` · menu sheet `{210, 22}`, backdrop `{200, 30}`, trail `{260, 24}` (delay 180) ·
spray underline: line `{300, 32}`, drips `{110, 9}` (160 + i × 70), dots `{420, 16}` ((i × 37) % 180),
fade-out `{260, 30}`.
- **Staggers**: headline 110 · card 90 · chip 70 · step 100 · sticker 120 · ledger 70 · drip 70
(+350) · stamp 160 (+200).
- **Seen rule**: `rootMargin: "0px 0px -15% 0px"`, or on screen at the page bottom
(`scrollY + innerHeight ≥ scrollHeight − 4`); nothing is watched until the preloader releases.
- **Parallax**: progress `(vh − top) / (vh + height)`, offset `progress − 0.5`, `y = offset × distance`
rem, `r = offset × tilt` deg, polled every 10 ms; default tilt 0.3°/rem.
- **Preloader**: `MIN_MS 1500`, give-up `3000`, `0.15 + loaded × 0.85` floor, progress `{480, 52}`,
`STOPS [0.05, 0.3, 0.55, 0.78]`, SLAP `{420, 20}` (from `scale(1.6) rotate(tilt − 10°)`), stamp
`{520, 22}` (from `scale(2.6) rotate(-24deg)` to `scale(1) rotate(-8deg)`), settle 175 ms,
`LIFT_DELAY 350`, lift `{480, 48}` to `translateY(-105%)`, `EXIT_MS 750`.
- **Hover**: colour swaps 250 ms `cubic-bezier(0.2, 0, 0, 1)`; header backdrop at `scrollY > 40`.
- **Grain**: `shared/texture-grain.webp` tiled at `32rem`, `opacity .2`, `mix-blend-mode: screen`,
`z-index 30` over the whole page.
## Assets
Every runtime asset lives under `ASSET_BASE_URL = https://storage.getlayers.ai/assets/held-mint-78f917a508`
at the same path it has under the project's `public/assets/`. Load each from that URL exactly as the
sections reference it (e.g. `https://storage.getlayers.ai/assets/held-mint-78f917a508/hero/hero-vault.png`).
Fonts are not assets — they come from the Google Fonts `<link>` above. The full list (87 files):
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-barbed-wire-arc.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-bullet-compact.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-bullet-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-bullet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-card.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-crown-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-crown-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-crown-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-crown.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-scribble-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-scribble-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-scribble-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/drop/drop-scribble.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/header/header-logo-mark-compact.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/header/header-logo-mark.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/header/icon-chevron.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/header/icon-menu.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/header/icon-user.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/hero/hero-scribble-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/hero/hero-scribble-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/hero/hero-scribble-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/hero/hero-scribble.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/hero/hero-vault.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/intro/intro-card-front.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/intro/intro-card-tucked-overlay.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/intro/intro-card-tucked.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/join/join-card.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/join/join-crown-compact.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/join/join-crown.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/join/join-scribble-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/join/join-scribble-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/join/join-scribble-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/join/join-scribble.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-crown-a.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-crown-b.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-crown-compact.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-scribble-1-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-scribble-1-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-scribble-1-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-scribble-1.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-scribble-2-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-scribble-2-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-scribble-2-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/process/process-scribble-2.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/questions/icon-close-compact.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/questions/icon-close.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/questions/icon-plus-compact.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/questions/icon-plus.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/questions/questions-scribble-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/questions/questions-scribble-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/questions/questions-scribble-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/questions/questions-scribble.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-crown-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-crown-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-crown-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-crown.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-ring-inner-laptop.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-ring-inner-mobile.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-ring-inner-tablet.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-ring-inner.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-ring-outer-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-ring-outer-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-ring-outer-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-ring-outer.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-scribble-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-scribble-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-scribble-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/record/record-scribble.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/shared/barbed-wire-strand.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/shared/crown-label-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/shared/crown-label-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/shared/crown-label-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/shared/crown-label.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/shared/crown-lg.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/shared/icon-arrow-compact.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/shared/icon-arrow.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/shared/texture-grain.webp
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-card-1.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-card-2.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-card-3.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-card-4.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-card-5.png
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-crown.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-scribble-laptop.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-scribble-mobile.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-scribble-tablet.svg
- https://storage.getlayers.ai/assets/held-mint-78f917a508/vault/vault-scribble.svg[/hidecontent]