Evolve
aadmin2026-11-05 发布Site模板9 阅读

Evolve

类型:Site模板 · 收录:2026-11-05 01:12:57 · 源码:会员可见

[hidecontent type="vip1"]
# Recreate this site as a single HTML file: Evolve

You are an expert creative front-end developer. Produce a **single self-contained `index.html`**
that reproduces the project below **exactly** — same layout, visuals, motion, and interaction.
Pure HTML/CSS/JS in one file: no build step, no framework, no bundler. Use ES modules with a CDN
importmap for Three.js 0.185.0. Hardcode every value given here as a fixed constant. Rebuild each
block described below as a section of the one file.

## What it is

A one-screen, jet-black hero for an AI infrastructure company. The subject is a **human head and
shoulders rendered as a point cloud** — tens of thousands of glowing particles lit by a blue key
light from front-left and a red rim light from back-right (magenta where they overlap), bloomed
into a soft haze that fills the frame. The cloud shimmers with a tiny per-point idle drift, turns
gently to follow the pointer (parallax), and particles scatter away from the cursor and spring
back behind it. Behind the subject: an eight-column hairline blueprint grid with small plus-shaped
crosshairs at rule intersections. Over it: an uppercase mono nav (PLATFORM · SOLUTIONS · RESEARCH ·
PRICING), a gradient-framed "GET STARTED" CTA with five cascading chevrons, a two-part display
headline — "Engineering / Intelligence" top-left, "That Evolves / With You." bottom-right — a mono
lead line bottom-left, and three grid-pinned stat labels whose figures settle out of scrambled
glyphs. Boot is a minimal cover: a mono percentage over a hairline track that fills with the
accent gradient, counting real milestones (fonts + first scene frame), then lifting. The whole
entrance is one authored timeline that begins only when the cover lifts.

## Page shell & libraries

- **Importmap:**

  ```html
  <script type="importmap">
  { "imports": {
    "three": "https://unpkg.com/three@0.185.0/build/three.module.js",
    "three/addons/": "https://unpkg.com/three@0.185.0/examples/jsm/"
  } }
  </script>
  ```

  From `three/addons/`: `postprocessing/EffectComposer.js`, `postprocessing/RenderPass.js`,
  `postprocessing/UnrealBloomPass.js`, `postprocessing/OutputPass.js`,
  `postprocessing/Pass.js` (for `Pass`/`FullScreenQuad`), `postprocessing/SavePass.js`.

- **Fonts** — two faces from Google Fonts:

  ```html
  <link rel="stylesheet"
        href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500&family=Space+Mono&display=swap" />
  ```

  → `font-family: "Inter Tight", system-ui, sans-serif` for the headline and CTA label;
  `font-family: "Space Mono", ui-monospace, monospace` for nav, stats, lead and the counter.

- **No scroll.** The hero is one `100dvh` section, `overflow: hidden`, background `#000000`.
  Layer order is **DOM order** (no z-index except the noted ones): rule grid → particle canvas
  (transparent, so the grid shows through *behind* the subject but the subject occludes it) →
  nav/CTA/headline/stats/lead → preloader cover (`z 50`).

- **Adaptive rem grid** — root font-size scales with the viewport, exactly:

  ```css
  html { font-size: 16px; }
  @media (max-width: 1920px) { html { font-size: 0.833333vw; } }
  @media (max-width: 1440px) { html { font-size: 1.111111vw; } }
  @media (max-width: 1024px) { html { font-size: 1.5625vw;   } }
  @media (max-width: 640px)  { html { font-size: 4.444444vw; } }
  ```

  The desktop composition (≥1280px) is pixel-matched to a 1440×800 frame — 1rem = design-px/16.

- **Colour tokens:**

  ```css
  :root {
    --background: #000000;
    --foreground: #ffffff;
    --foreground-muted: rgb(255 255 255 / 0.4);   /* stat labels            */
    --rule: rgb(255 255 255 / 0.15);              /* inner grid rules       */
    --rule-strong: rgb(255 255 255 / 0.2);        /* outer rules + stubs    */
    --marker: #666666;                            /* plus crosshairs        */
    --accent-start: #8dd8ff;
    --accent-middle: #69beff;
    --accent-end: #4c97ff;
    --gradient-accent: linear-gradient(90deg,
      var(--accent-start) 0%, var(--accent-middle) 45%, var(--accent-end) 100%);
    --scene-key: #2f6bff;                         /* particle key light     */
    --scene-rim: #ff1f4a;                         /* particle rim light     */
    --scene-fill: #0a1430;                        /* particle ambient fill  */
    --hairline: 1px;                              /* never rem — stays crisp */
    --duration-fast: 150ms; --duration-normal: 250ms;
  }
  ```

- **Type scale** (desktop, rem): display `5.75rem` line-height 1 (Inter Tight 400); lead
  `1.125rem` lh 1.2, letter-spacing −0.03em; nav `1rem` lh 1.3 uppercase; meta/stats `0.75rem`
  lh 1.2 uppercase. Below 1280 use fluid sizes: display `clamp(42px, 6.3889vw, 82px)`, lead
  `clamp(14px, 1.25vw, 16px)`, nav `clamp(13px, 1.1111vw, 14.22px)`, meta
  `clamp(12px, 1.05vw, 14px)`.

## The device tiers

Resolved once at boot from `innerWidth`: `mobile` ≤ 767, `tablet` ≤ 1279 (also any wider viewport
with a coarse pointer), else `desktop`.

| | maxPixelRatio | point stride | frame budget | point size × | bloom | framing override |
|---|---|---|---|---|---|---|
| mobile | 0.85 | keep every 4th | 1000/30 ms | 0.68 | off | y −0.62, scale 0.78, camera 4.3 |
| tablet | 1.25 | every 2nd | 1000/45 ms | 1.1 | on | y −0.62, scale 1.24, camera 4.0 |
| desktop | 1.5 | every point | uncapped | 1 | on | none — authored config verbatim |

- The vertex buffer is **spatially sorted by X**, so down-sampling must **stride, never
  truncate** — slicing the tail deletes one whole side of the head.
- Pointer effects (parallax, scatter) only on `(pointer: fine)`.
- On `prefers-reduced-motion` (or a mobile device with Data Saver / ≤2 GB memory): render **one
  static frame** and never start the loop.
- The render loop also stops while the tab is hidden.

## The WebGL scene — the particle head

### Data

Fetch `vertices.json` (see Assets): `{ "objects": { "<name>": { "verts": [x,y,z, …] } } }` — one
flat vertex array, **Blender Z-up with the face toward −Y**. Convert each vertex to three.js
space as `[x, z, -y]`. The cloud is ~1.85 × 1.9 × 1.09 world units.

Per kept point (after striding), build three attributes:

- `position` — the converted vertex.
- `aNormal` — the source has no normals; approximate: direction from the (strided) centroid to
  the point, with the **Y component multiplied by 0.35** before normalising. Reads convincingly
  on a bust.
- `aRandom` — three uniform randoms in [−1, 1], fixed per point.

`geometry.computeBoundingSphere()` — the depth fade band is anchored to it. On the `Points`
object set `frustumCulled = false` (positions are displaced in the shader).

### Renderer, camera, composer

- `WebGLRenderer({ antialias: false, alpha: true, stencil: false, powerPreference: desktop ?
  "high-performance" : "default" })`; clear colour `#000000` at **alpha 0** — the canvas is
  transparent so the rule grid sits behind it and is occluded by the subject. Clamp
  `setPixelRatio` to the tier (hard ceiling 1.5), on **both** renderer and composer.
- `PerspectiveCamera(fov 32, aspect, 0.1, 100)` at `z = 3.4` (camera distance; tier framing may
  override to 4.0/4.3). Never rotated.
- The cloud sits in a `Group` at `(0.04, −0.37, 0)` (tier framing overrides y), scale `1.33`
  (tier overrides), resting rotation `(0°, 24°, 0°)`.
- Composer chain: `RenderPass` → `UnrealBloomPass(strength 1.23, radius 0.4, threshold 0.2)` →
  the **temporal filter pair** (below) → `OutputPass`.
- **Bloom runs at half the CSS size**: `bloomPass.setSize(clientWidth * 0.5, clientHeight * 0.5)`
  — deliberately coarse; the blur smears the glow into the atmospheric haze that covers most of
  the frame. Do not size it off the drawing buffer.
- Point size: `uScale = clientHeight * pixelRatio * 0.5`;
  `uSize = 4.6 * tierPointSizeScale * pixelRatio * cameraDistance / uScale`. In the shader,
  `gl_PointSize = uSize * (uScale / -viewPosition.z)`.

### The shaders (verbatim)

Vertex:

```glsl
attribute vec3 aNormal;
attribute vec3 aRandom;

uniform float uSize;
uniform float uScale;
uniform float uTime;

uniform vec3  uCursor;
uniform float uScatterRadius;
uniform float uScatterForce;
uniform float uTurbulence;

uniform float uDriftAmount;
uniform float uDriftSpeed;

uniform float uDepthNear;
uniform float uDepthFar;
uniform float uDepthStart;
uniform float uDepthStrength;

uniform vec3  uKeyColor;
uniform vec3  uRimColor;
uniform vec3  uFillColor;

varying vec3  vColor;
varying float vFade;

const vec3 KEY_DIR = vec3(-0.55, 0.25, 0.79);
const vec3 RIM_DIR = vec3(0.85, 0.08, -0.52);

const float KEY_WRAP = 0.5;
const float KEY_FALLOFF = 2.2;
const float KEY_INTENSITY = 1.5;
const float RIM_FALLOFF = 1.6;
const float RIM_INTENSITY = 1.3;

const float TAU = 6.2831853;

vec3 driftOffset(vec3 normal, vec3 seed, float t) {
  vec3 guide = abs(normal.y) > 0.9 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
  vec3 tangent = normalize(cross(normal, guide));
  vec3 bitangent = cross(normal, tangent);

  return tangent   * sin(t * (1.00 + seed.x * 0.40) + seed.x * TAU)
       + bitangent * sin(t * (0.83 + seed.y * 0.40) + seed.y * TAU) * 0.9
       + normal    * sin(t * (0.61 + seed.z * 0.40) + seed.z * TAU) * 0.3;
}

void main() {
  vec3 normal = normalize(aNormal);
  float key = pow(
    max(0.0, dot(normal, normalize(KEY_DIR)) * KEY_WRAP + (1.0 - KEY_WRAP)),
    KEY_FALLOFF
  ) * KEY_INTENSITY;
  float rim = pow(
    max(0.0, dot(normal, normalize(RIM_DIR))),
    RIM_FALLOFF
  ) * RIM_INTENSITY;

  vColor = min(vec3(1.0), uFillColor + uKeyColor * key + uRimColor * rim);

  vec3 displaced = position;

  if (uDriftAmount > 0.0) {
    displaced += driftOffset(normal, aRandom, uTime * uDriftSpeed) * uDriftAmount;
  }

  if (uScatterForce > 0.0) {
    vec3 away = displaced - uCursor;
    float dist = length(away);
    float influence = 1.0 - smoothstep(0.0, uScatterRadius, dist);
    if (influence > 0.0) {
      vec3 direction = normalize(away + aRandom * uTurbulence + vec3(1e-5));
      displaced += direction * influence * uScatterForce;
    }
  }

  vec4 viewPosition = modelViewMatrix * vec4(displaced, 1.0);

  float through = clamp(
    (-viewPosition.z - uDepthNear) / max(uDepthFar - uDepthNear, 1e-4),
    0.0,
    1.0
  );
  vFade = 1.0 - uDepthStrength * smoothstep(min(uDepthStart, 0.99), 1.0, through);

  gl_PointSize = uSize * (uScale / -viewPosition.z);
  gl_Position = projectionMatrix * viewPosition;
}
```

Fragment:

```glsl
varying vec3  vColor;
varying float vFade;

const float MIN_ALPHA = 0.004;

void main() {
  vec2 offset = gl_PointCoord - 0.5;
  float distanceSquared = dot(offset, offset);
  if (distanceSquared > 0.25) discard;

  float alpha = smoothstep(0.25, 0.02, distanceSquared) * vFade;
  if (alpha < MIN_ALPHA) discard;

  gl_FragColor = vec4(vColor, alpha);

  #include <colorspace_fragment>
}
```

`ShaderMaterial({ transparent: true, depthWrite: true })`. Uniforms at their authored defaults:
`uScatterRadius 0.34`, `uTurbulence 1`, `uDriftSpeed 1`, `uDriftAmount 0.003` (0 when frozen),
`uDepthStart 0.35`, `uDepthStrength 0.85`, `uKeyColor #2f6bff`, `uRimColor #ff1f4a`,
`uFillColor #0a1430`. (`#include <colorspace_fragment>` is three's output-encoding chunk — keep
it or the colours read muddy.)

### Per-frame behaviour

- **Parallax**: track the pointer in the section's NDC, clamped to [−1, 1] on both axes (the
  listener is on `window`). Ease `eased += (pointer − eased) * 0.04` per frame. Rotation:
  `pitch = 0° + eased.y * 0.28 rad`, `yaw = 24° + eased.x * 0.45 rad`, roll 0.
- **Scatter**: when the pointer is inside the canvas and scatter is on, ramp `scatterAmount`
  toward 1 (ease 0.09; toward 0 when it leaves). Project the eased pointer onto the model's
  z-plane (ray from camera), lerp a smoothed cursor toward it (same 0.09), convert to the
  group's **local** space every frame (parallax rotates it), and set `uScatterForce =
  0.2 * scatterAmount`.
- **Depth fade band** (per frame, after the group is placed): centre = the bounding sphere's
  centre through the group's world matrix; `centreDepth = camera.z − centre.z`;
  `halfBand = sphereRadius * group.scale.x`; `uDepthNear = centreDepth − halfBand`,
  `uDepthFar = centreDepth + halfBand`.
- **Idle skip**: compute the subject's screen motion this frame in CSS px —
  `(|Δpitch| + |Δyaw|) * 1` (subject radius) plus the scatter-force/cursor deltas plus the
  drift's own peak rate (`0.003 * 1 * scale * Δt` while drifting), all × pixels-per-world-unit
  (`clientHeight / (2 * distance * tan(fov/2))`). Deltas are measured against the **last drawn**
  frame. If a previous frame exists and motion < **0.05 px**, skip the draw entirely.

### The temporal filter (anti-flicker — part of the look's stability)

A dense point cloud flickers two ways; both are fixed at the end of the composer without touching
the artwork:

1. **Frame blend.** Keep a `SavePass` of the final frame. Before it, a full-screen shader pass
   mixes the current frame with the saved one:
   `gl_FragColor = mix(frame, lastFrame, uFrameMix)` where per drawn frame
   `uFrameMix = 1 − (0.22 + 0.78 * smoothstep(motionPx, 0, 1.5))` — i.e. heavy history when the
   subject is barely moving (weight of new frame bottoms out at 0.22), **zero history at ≥1.5 px
   of motion** so movement never smears. After a resize, context loss or one-off render, reset
   `uFrameMix` to 0 for that frame.
2. **Glow smoothing.** Bloom's finished glow lives in its own buffer before being added
   (`bloomPass.renderTargetsHorizontal[0]`). Keep a ping-pong average of it:
   `history = mix(history, glow, 0.15)` (weight 1 on the first frame after an invalidate), sized
   to bloom's own buffer. In the frame-blend shader, subtract the *excess* of raw glow over the
   averaged glow: `frame −= (texture(tGlow, uv) − texture(tGlowHistory, uv))` — the raw glow
   cancels exactly and only the steadied one remains. Skip the correction entirely when bloom is
   off (mobile).

Both passes use `NoBlending` and a pass-through vertex shader (`vUv = uv`).

### Boot

Fetch the JSON, build the geometry, `renderer.compile(scene, camera)`, draw **one frame through
the composer** (no history blend), then report ready to the preloader. If WebGL is unavailable or
the fetch fails, report nothing — the cover's failsafe opens the page without a subject.

## Layout & sections (desktop, 1440×800 reference; all rem)

The section is `position: relative; height: 100dvh; overflow: hidden`.

### The rule grid (behind the canvas, `aria-hidden`)

- Vertical hairlines (1px wide, full height) at 0% and 100% in `--rule-strong`; at 12.5%, 25%,
  37.5%, 50%, 62.5%, 75% in `--rule`.
- The 87.5% rule is **split around the CTA**: a `--rule-strong` stub from the top, `1rem` tall;
  then a `--rule` line from `top: 4.375rem` to the bottom.
- One horizontal `--rule` hairline at `top: 74.875%`.
- Eight plus-shaped crosshairs (11×11 SVG, `path "M6 5H11V6H6V11H5V6H0V5H5V0H6V5Z"`, fill
  `--marker`, `0.6875rem` square, centred on the intersection) at: (75%, 1.9375%),
  (37.5%, 11.6875%), (87.5%, 25.8125%), (25%, 50.9375%), (75%, 50.9375%), (12.5%, 74.875%),
  (87.5%, 74.875%), (25%, 99.3125%). That order is the reveal order.

### Nav (top-left)

`position: absolute; top: 2.3125rem; left: 2.5rem; mix-blend-mode: difference`. A row of four
uppercase Space Mono links, gap `3rem`: PLATFORM · SOLUTIONS · RESEARCH · PRICING (`#platform`
etc.). Hover: opacity 0.7, 150ms.

### Scroll cue (top-centre)

A small 27×11 chevron SVG (see Assets — `scroll-cue.svg`) at `top: 2.3125rem`, horizontally
centred.

### CTA (top-right)

`top: 1rem; right: 2.5rem` — a `12.75rem × 3.375rem` box:

- **Hairline gradient frame**: a 1px border carrying `--gradient-accent` (pseudo-element with
  `padding: 1px; background: var(--gradient-accent);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude`).
- Label "GET STARTED", Inter Tight 500, `1rem`, uppercase, letter-spacing 0.015em,
  `padding-left 1.5rem` — filled with the SAME ramp via `background-clip: text` with
  `background-size: 12.75rem 100%; background-position: -1.5rem 0`, so frame, label and chevrons
  all read one continuous gradient across the button.
- Five chevrons on the right (`left: 8.625rem`, a 42×15 SVG, viewBox `138 19.5 42 15`), each the
  path `M138 34.1444L138.614 34.5L144 26.9725L138.614 19.5L138 19.8556L138 26.1513L138.818
  26.9725L138 27.7937L138 34.1444Z` translated by `9` SVG units per index, filled with a
  `userSpaceOnUse` linearGradient x1=0 → x2=204 of the same three accent stops (offsets 0 /
  0.45 / 1).
- **Hover**: a fill layer (`--gradient-accent` at opacity 0.18) wipes in — `transform:
  scaleX(0→1)`, origin left, 420ms on `cubic-bezier(0.16, 1, 0.3, 1)`; the chevrons run a
  looping cascade — each translates +4 SVG units and back (reverse loop), 45ms stagger per
  chevron, 420ms per leg. On leave, stop the loop and return every chevron to 0.

### Headline

One `<h1>` (Inter Tight 400, `5.75rem`, line-height 1, `pointer-events: none`), two absolute
pairs of hard-broken lines:

- "Engineering" / "Intelligence" — `top: 8.625rem; left: 2.5rem`, left-aligned.
- "That Evolves" / "With You." — `right: 2.5rem; bottom: 2.5rem`, right-aligned.

### Stats (grid-pinned, Space Mono `0.75rem`, uppercase, `--foreground-muted`, line-height 1.2)

Each sits `0.625rem` right of its rule: at `left: calc(87.5% + 0.625rem); top: 27.125%`, width
`7.125rem`: "99.99%" / "Platform uptime" (two lines); at `calc(75% + 0.625rem); top: 52.25%`,
width `7.125rem`: "45B+ Tokens processed daily"; at `calc(12.5% + 0.625rem); top: 70.875%`, width
`9.375rem`: "120+" / "Enterprise customers".

### Lead (bottom-left)

`bottom: 2.5rem; left: 2.5rem; width: 14.5625rem`, Space Mono `1.125rem`, line-height 1.2,
letter-spacing −0.03em: "From foundation models to production-ready infrastructure."

### Responsive

Below 1280: fluid type sizes; grid at 50% opacity. Below 768: the section becomes a flex column
(padding 1rem, top 5.5rem) — nav collapses into a 48px bordered toggle (three bars morphing to an
×) opening a dropdown menu, the CTA stays visible top-right at 48px tall, the headline collapses
into one left-aligned block, stats join the flow as single rows, the lead drops to the bottom;
the grid is hidden and a legibility scrim (vertical gradient, black 0.78 → transparent 34–58% →
black 0.55 at 78% → 0.78) sits over the subject.

## The loader / reveal

A fixed cover (`z 50`), background `#000000`, centred column `min(70vw, 26rem)` wide: a Space
Mono percentage (`0.75rem`, uppercase, `--foreground-muted`) above a 1px `--rule` track whose
fill (`--gradient-accent`) scales `scaleX(0→progress)`, origin left, 420ms per step on
`cubic-bezier(0.16, 1, 0.3, 1)`.

Progress counts **two real milestones**, not a timer: `document.fonts.ready` and the scene's
first rendered frame. Each advances the bar by half. Failsafe: at **6 s** force 100% regardless
(no WebGL must still open the page). At 100%, hold **260 ms**, then lift: opacity → 0 and
`translateY(−24px)` over 620ms on the same curve, then remove from the DOM.

**The entrance timeline is gated on the lift** — every animated element holds at its `from` state
until the cover has lifted, then runs its own delay.

## Entrance choreography

Shared curve `cubic-bezier(0.16, 1, 0.3, 1)` (call it EASE). "Fade" = opacity 0→1 +
translateY(±0.75rem→0), 700ms on EASE. Delays are ms after the cover lifts:

| element | animation | delay |
|---|---|---|
| nav | fade from above | 0 |
| scroll cue | fade from above | 0 |
| rule grid | plain opacity fade, 700ms | 200 |
| crosshairs | each: opacity 0→1 + `scale(0) rotate(−45deg)` → `scale(1) rotate(0)`, 700ms EASE | 200 + index·40 |
| particle head | wrapper fades in 700ms; inside it, `scale(1.06) → scale(1)` over **2500ms** EASE | 400 |
| "Engineering"/"Intelligence" | letter-by-letter (below) | 600 (+150 for line 2) |
| "That Evolves"/"With You." | letter-by-letter | 1400 (+150 for line 2) |
| stats | fade from below; figures scramble (below) | 2000 |
| lead copy | line-by-line: same letter shape but per wrapped line, 1000ms, 130ms stagger | 2000 |
| CTA | fade from above | 2400 |

- **Headline letters**: each glyph wrapped in a span; from `translateY(0.42em) + opacity 0 +
  blur(10px)` to rest, **1150ms** on EASE, **22ms** stagger along the line, each line starting
  **150ms** after the one above. No overflow clipping — blur and opacity carry the reveal.
- **Stat scramble**: the leading figure of each stat (`99.99%`, `45B+`, `120+`) renders as random
  glyphs from `0123456789#%$&*+<>/` (same length, so the mono line never reflows), then settles
  left-to-right over **700ms**, characters locking in as progress crosses their index ('.' and
  ',' never scramble). The real text must be in the DOM before JS touches it.
- Under `prefers-reduced-motion`, every entrance jumps straight to its end state and the scramble
  never runs.

## Fixed parameters (bake these in)

```
SCENE     camera fov 32 dist 3.4 (tablet 4.0, mobile 4.3) · model (0.04, −0.37, 0)
          scale 1.33 (tablet 1.24, mobile 0.78) · modelY tablet/mobile −0.62 · rotY 24°
COLORS    key #2f6bff · rim #ff1f4a · fill #0a1430 · bg #000000
LIGHTS    KEY_DIR (−0.55, 0.25, 0.79) wrap 0.5 falloff 2.2 ×1.5
          RIM_DIR (0.85, 0.08, −0.52) falloff 1.6 ×1.3
POINTS    size 4.6 css-px (× tier 1 / 1.1 / 0.68) · stride 1/2/4 · soft disc, edge smoothstep
BLOOM     strength 1.23 radius 0.4 threshold 0.2 · sized at 0.5 × CSS size · off on mobile
TEMPORAL  idle skip < 0.05 px · min new-frame weight 0.22 · full motion 1.5 px
          glow average weight 0.15 · subject radius 1
SCATTER   force 0.2 · radius 0.34 · turbulence 1 · ease 0.09
DRIFT     amount 0.003 · speed 1 · surface-tangent sines (0.61–1.4 rad/s per point)
DEPTH     start 0.35 · strength 0.85 · band = bounding sphere through group matrix
PARALLAX  yaw 0.45 rad · pitch 0.28 rad · ease 0.04 · pointer clamped to ±1
DPR       ≤1.5 (desktop) / ≤1.25 (tablet) / ≤0.85 (mobile) · fps uncapped/45/30
TIMELINE  nav 0 · grid 200 · visual 400 · titleLead 600 · titleTrail 1400 · stats 2000
          cta 2400 · EASE cubic-bezier(0.16,1,0.3,1) · fade 700ms lift 0.75rem
LETTERS   1150ms · 22ms stagger · +150ms/line · from y 0.42em blur 10px
LEAD      1000ms/line · 130ms stagger        SCRAMBLE 700ms glyphs 0123456789#%$&*+<>/
CTA       12.75rem × 3.375rem · ramp #8dd8ff → #69beff 45% → #4c97ff · fill 0.18 wipe 420ms
          chevrons ×5 · offset 9 · travel 4 · stagger 45ms
GRID      rules 12.5% steps · strong 20% white · inner 15% · horizontal 74.875%
          split rule 87.5% (stub 1rem, resume 4.375rem) · markers #666 11×11 stagger 40ms
LOADER    milestones fonts + first frame · failsafe 6s · settle 260ms · lift 620ms −24px
TYPE      display 5.75rem lh 1 · lead 1.125rem −0.03em · nav 1rem · meta 0.75rem
LAYOUT    margins 2.5rem · nav/cue top 2.3125rem · CTA top 1rem · headline top 8.625rem
```

## Copy (verbatim)

- Nav: `Platform` · `Solutions` · `Research` · `Pricing`; CTA: `Get Started`
- Headline: `Engineering` / `Intelligence` — then `That Evolves` / `With You.`
- Lead: `From foundation models to production-ready infrastructure.`
- Stats: `99.99%` Platform uptime · `45B+ Tokens processed daily` · `120+` Enterprise customers

## Assets

Load these exact URLs:

- Vertex buffer (~1.9 MB JSON): https://storage.getlayers.ai/assets/evolve-f940e12f08/vertices.json
- Scroll cue: https://storage.getlayers.ai/assets/evolve-f940e12f08/scroll-cue.svg

(Fonts come from Google Fonts — see "Page shell & libraries".)

If `vertices.json` fails to load, show a small visible notice over the black field (never a
silent empty hero), and still open the page via the loader's 6 s failsafe.
[/hidecontent]
← 返回首页