# PROMPTRIMWHEEL · WordPress edition — Elementor page on demo.uichemy.com
**This is not a prompt. Do not paste it into a chat box.**
This is the WordPress build of `RIM-WHEEL-MASTER-PROMPT.md`: the same page as one UiChemy Composer widget, with every image in the WP media library, matched px for px against the standalone files.
| | |
|---|---|
| page | **397** · published · `https://demo.uichemy.com/halfsecond-every-sport-one-place/` |
| widget | `aylzqhn` (label "Halfsecond"), wrapper `.uichemy-halfsecond-1` |
| code file | `/wp-content/uichemy-composer/halfsecond.js?v=1` |
| media | 11 WebP in `/wp-content/uploads/2026/09/` (sp-01-lane … sp-10-notout, foot-track), sideloaded by `upload_images` |
| fonts | Archivo (ital, wdth 62–125, wght 100–900) + Barlow Condensed 400/500/600, site head |
## Rules this build follows (each one is a trap found on earlier ports)
1. **The section holds markup and CSS only. Its `js` field stays empty.** An editor save strips section JS, so script.js ships as a code file.
2. **No comments in section CSS.** UiChemy's normaliser cuts a comment at its first `;` and the rest of the sheet disappears.
3. **Every `@media` block opens with `.uichemy-halfsecond-1{--hs-guard:0}`.** The formatter mangles the first selector of each block (`:hover` becomes `: hover`).
4. **`#fff` is written as `rgb(255, 255, 255)`.** The global-colour matcher would otherwise rewrite it to a theme token.
5. **Keyframes are renamed `hs-twblink / hs-arrive / hs-cuespin / hs-cuedrop`,** so they can't collide with other pages' names.
6. **Every rule that depends on a class on `<html>` lives in site head, not the section.** That covers `html.js`, `is-loading/ready/done`, `html:not(.js)`, `.js .scene__pin` and the `html`/`body` rules. A section can't select its ancestors.
- The classes are renamed `hs-js / hs-loading / hs-ready / hs-done`, so they're unique site-wide.
- Selectors carry the wrapper class four times. The plugin prefixes section rules with the element id, and doubles that prefix inside media queries. A state rule must still outrank both, as it does in the source.
7. **The pre-paint class script sits in site head,** so the loader and the start states are right on the first frame. It sets nothing under `?elementor-preview`, so the editor shows the static no-JS layout instead of a stuck loader.
8. **`halfsecond.js` is script.js verbatim except for 7 lines:** the four class renames, the loader wait, and `document.images` scoped to the wrapper. It's wrapped in a guard that returns on any page without the section and inside the editor.
9. **`overflow-x: hidden` stays on `body` (site head), never the wrapper.** On the wrapper it kills both sticky pins. `.why` and `.voices` keep `overflow-x: clip` from the standalone fix.
10. **Cache.** Cloudflare caches `/wp-content/uichemy-composer/*.js` for 30 days. After editing the code file, bump `?v=1` in B2 using `set-site-code` (`update-site-code` only appends).
## Verified (Playwright, live against the standalone)
- At **1440×900, 1920×1080, 1024×768, 768×1024 and 390×844** these matched: every section box (scene, pin, plate, nav, CTA, why, cards, figures, ledger, voices, FAQ, foot), font size / family / weight / stretch / line-height / letter-spacing / colour / margins on 11 text elements, and the wheel progress, pin top and first three plate rects at 30% and 60% of the scene. The display lines matched too (867px / 547px) once the webfont had swapped in. Before that, both sides measure the fallback font.
- Images load from `demo.uichemy.com` only. Zero console errors. `scrollWidth` equals the viewport.
- The FAQ keeps one item open (`001000` after clicking the third). Figures index `2` at mid-scene with the pin at top `0`. 37 reveal targets.
- Reduced motion: no loader, 0 hidden reveals. With JS off or in editor preview: no loader, 0 hidden reveals, static layout.
- MASSIF (page 395) still loads clean: same height (19022), no errors.
## Appendix · the five pieces, verbatim
| block | sha256 |
|---|---|
| B1 · site_before_head (append) | `99d179096bf4e7ddba7309be6e2b00beb17921820ac81ad59bfa92e6601e1b99` |
| B2 · site_before_body (append) | `8d0be1292b06ef734a25edca7c2e170427aeae78ff81fb43442640e8a08a3059` |
| B3 · code file `halfsecond.js` | `7adabf46dbcb14a8bc6081e06fc55ff53107b90f01c3e332b9928e1aededdf7c` |
| B4 · section HTML (WP-media URLs, as stored) | `80e0e128f2be38b72db5c0a4b46194f45f2a6fe6ee6db7ace2b1393d8fb78d11` |
| B5 · section CSS (as sent) | `4480a2fcdb4b6ae07a9f87602f716fd1e0f165b5666a6134decdc3a53083126e` |
### B1 · site_before_head (append)
```html
<link href="https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&family=Barlow+Condensed:wght@400;500;600&display=swap" rel="stylesheet" />
<script>
(function (r) {
if (/elementor-preview/.test(location.search)) return;
r.classList.add('hs-js');
if (!matchMedia('(prefers-reduced-motion: reduce)').matches) r.classList.add('hs-loading');
}(document.documentElement));
</script>
<style>
html:has(.uichemy-halfsecond-1) { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
html:has(.uichemy-halfsecond-1) body { margin: 0; overflow-x: hidden; background: #0C0D0F; }
html.hs-js .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .scene__pin .plate { will-change: transform; }
html.hs-js .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 [data-reveal] { opacity: 0; transform: translate(var(--rx, 0px), var(--ry, 20px)); }
html.hs-js .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 [data-reveal].is-in { opacity: 1; transform: none; transition: opacity .85s var(--in-ease) var(--d, 0s), transform .85s var(--in-ease) var(--d, 0s); }
@media (prefers-reduced-motion: reduce) {
html:has(.uichemy-halfsecond-1) { scroll-behavior: auto; }
html.hs-js .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 [data-reveal] { opacity: 1; transform: none; }
html.hs-js .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .voices__say { transform: none; transition: none; }
}
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .figures, html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .figures__pin { height: auto; position: static; overflow: visible; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .figures__num, html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .figures__lab { height: auto; overflow: visible; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .figures__bar { display: none; }
html.hs-js .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .voices__say { transform: translateX(var(--sx, 0px)); transition: transform .95s var(--in-ease) .1s; }
html.hs-js .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .voices__item.is-in .voices__say { transform: none; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .faq__a { grid-template-rows: 1fr; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .faq__a p { opacity: 1; transform: none; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .faq__mark::after { transform: scaleY(0); }
html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .load { display: block; position: fixed; inset: 0; z-index: 90; background: var(--bg); }
html.hs-ready .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .load { display: block; position: fixed; inset: 0; z-index: 90; background: var(--bg); transform: translateY(-101%); transition: transform .82s cubic-bezier(.76,0,.24,1); }
html.hs-done .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .load { display: none; }
html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .nav, html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .note, html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .cta, html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .scrollcue { opacity: 0; }
html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .nav, html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .note, html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .cta { transform: translateY(14px); }
html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .rings { opacity: 0; }
html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .type__line--a span { transform: translateX(-32vw); }
html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .type__line--b span { transform: translateX(32vw); }
html.hs-ready .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .nav { transition: opacity .7s var(--in-ease) .05s, transform .7s var(--in-ease) .05s; }
html.hs-ready .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .rings { transition: opacity 1.4s var(--in-ease) 0s; }
html.hs-ready .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .type__line span { transition: transform 1.25s var(--in-ease) .1s; }
html.hs-ready .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .note { transition: opacity .7s var(--in-ease) .58s, transform .7s var(--in-ease) .58s; }
html.hs-ready .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .cta { transition: opacity .7s var(--in-ease) .66s, transform .7s var(--in-ease) .66s; }
html.hs-loading .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .scrollcue { transform: translate(-50%, -50%) scale(.72); }
html.hs-ready .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .scrollcue { transition: opacity .6s var(--in-ease) .95s, transform .9s var(--in-ease) .95s; }
@media (max-width: 860px) {
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .work__grid > :nth-child(n+7) { display: block; }
}
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .scene { height: auto; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .scene__pin { position: static; height: auto; overflow: visible; padding: clamp(110px, 17vh, 190px) var(--gutter) clamp(60px, 9vh, 110px); }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .rings { display: none; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .type { position: static; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .type__line { opacity: .82; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .type__line { position: static; transform: none; width: auto; white-space: normal; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .scrollcue { display: none; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .work { position: static; transform: none; width: 100%; margin: clamp(36px, 6vh, 70px) 0; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .work__grid::before { display: none; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .note { position: static; margin: 0 0 18px; }
html:not(.hs-js) .uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1.uichemy-halfsecond-1 .cta { position: static; align-items: flex-start; margin: 0 0 18px; }
</style>
```
### B2 · site_before_body (append)
```html
<script src="/wp-content/uichemy-composer/halfsecond.js?v=1"></script>
```
### B3 · code file `halfsecond.js`
```js
/* Halfsecond on WordPress — script.js verbatim inside one guard.
Runs only on a page that carries the section, and never inside the
Elementor editor preview (the head script sets no hs-* classes there, so
the widget shows its static no-JS layout for editing). State classes live
on <html> as hs-js / hs-loading / hs-ready / hs-done. */
(function () {
if (!document.querySelector('.uichemy-halfsecond-1')) return;
if (/elementor-preview/.test(location.search) || document.body.classList.contains('elementor-editor-active')) return;
/* ============================================================================
Halfsecond — all page behaviour, in the order the page used to load it.
Five independent IIFEs, concatenated. They were five files while the page
was being built; they are one file now because a landing page that ships as
three files is easier to hand over than one that ships as seven. Nothing
else changed — no renaming, no minifying, no merging of scopes. Each still
talks to the others only through the DOM and a class on <html>.
1 · BOOT the loader, and the handover to the hero's entrance
2 · WHEEL the pinned hero: ten plates on a rim, two display lines
3 · FIGURES the pinned four-number scene
4 · REVEAL reveals, counters, typewriter, parallax
5 · FAQ the accordion
Order matters only in that BOOT must be able to set the loader up before
REVEAL waits on it; every other pair is independent.
========================================================================== */
/* ============================================================================
1 · BOOT
============================================================================ */
/* ============================================================================
Boot — the loader, and the handover to the hero's entrance.
Why a loader at all: the ten plates load eagerly and all of them are on
screen inside the first viewport of scrolling. Revealing the page after the
first image decodes means somebody scrolls straight into nine empty frames,
so the gate waits for the lot — then the CSS entrance runs against content
that is already there.
Three rules it has to obey:
1. Never trap anyone. MAX_MS is a hard cap: if an image 404s, a font hangs
or the network dies, the page reveals anyway. The loader is a courtesy,
not a gate.
2. Never flash. MIN_MS holds it long enough to read on a fast connection —
a loader that appears and vanishes in 80ms is worse than none.
3. Never run for anyone who asked it not to. The `is-loading` class is set
in the document head only when reduced motion is off, so for those
readers this file has nothing to do and the loader never renders.
========================================================================== */
(function () {
'use strict';
var root = document.documentElement;
if (!root.classList.contains('hs-loading')) return; /* reduced motion */
var MIN_MS = 700; /* shortest the loader is allowed to be on screen */
var MAX_MS = 5000; /* longest, whatever the network is doing */
var HOLD = 260; /* beat at 100% before the panel lifts */
var num = document.querySelector('[data-load-num]');
var bar = document.querySelector('[data-load-bar]');
var imgs = [].slice.call(document.querySelectorAll('.uichemy-halfsecond-1 img'));
var total = imgs.length + 1; /* + 1 for the webfonts */
var done = 0;
var start = Date.now();
var revealed = false;
function paint() {
var pct = Math.min(100, Math.round(done / total * 100));
if (num) num.textContent = pct < 10 ? '0' + pct : String(pct);
if (bar) bar.style.transform = 'scaleX(' + (pct / 100).toFixed(3) + ')';
}
function reveal() {
if (revealed) return;
revealed = true;
if (num) num.textContent = '100';
if (bar) bar.style.transform = 'scaleX(1)';
setTimeout(function () {
root.classList.remove('hs-loading');
root.classList.add('hs-ready');
/* the panel is out of the layout once it has finished lifting, so it
can never swallow a click on the button underneath it */
setTimeout(function () { root.classList.add('hs-done'); }, 900);
}, HOLD);
}
function bump() {
done++;
paint();
if (done >= total) {
setTimeout(reveal, Math.max(0, MIN_MS - (Date.now() - start)));
}
}
paint();
imgs.forEach(function (im) {
if (im.complete) return bump();
im.addEventListener('load', bump, { once: true });
im.addEventListener('error', bump, { once: true }); /* a 404 still counts */
});
if (document.fonts && document.fonts.ready) document.fonts.ready.then(bump);
else bump();
setTimeout(reveal, MAX_MS); /* rule 1 */
})();
/* ============================================================================
2 · WHEEL
============================================================================ */
/* ============================================================================
The wheel.
Ten plates ride the rim of a giant wheel that rolls up through the frame,
then peel off one at a time into the grid they already occupy in the markup.
---- the geometry -------------------------------------------------------
The wheel is described in SCREEN pixels, not world units, because one
quantity drives everything: `flyW`, the width a plate should have on screen.
R = flyW / SPACING radius — the arc grows with the plates
a = spin + (mid - i) * SPACING
x = cx + R * sin(a)
y = crest + R * (1 - cos(a)) crest = the top of the wheel
rot = a tangent, so plates follow the rim
SPACING is the angle one plate subtends, and it is CONSTANT: arc-length
spacing equals the plate's own width, so the ribbon is seamless at every
size. Because R is tied to flyW, "the plates get bigger" and "the arc opens
out" are the same gesture — which is what a ring approaching a camera
actually does, and why this reads as depth rather than as a 2D zoom.
Two smaller terms sell the third dimension: plates away from the crest lean
toward the viewer (`tz`) and turn slightly (`ry`), so the CSS perspective on
.scene__pin draws them as trapezoids rather than flat rectangles.
---- the landing --------------------------------------------------------
Every plate is absolutely positioned inside its grid slot, so its resting
transform is `none` and CSS alone produces the finished grid. This file only
computes the delta OUT to the rim, and eases that delta back to zero, one
plate at a time. Nothing here has to know what the grid looks like: the slot
is measured live, so the landing stays correct through a resize, a font
swap, or a late image decode.
---- what makes it smooth -----------------------------------------------
Three things, and all three matter. Take any one out and the scene gets a
visible hitch:
1. The scene follows a DAMPED copy of the scroll position, not the scroll
position itself. A wheel notch arrives as one large jump; welded
straight to it, ten plates teleport. `cur` chases `target` at a fixed
time constant, so a notch becomes a short glide.
2. Tracks are read with a MONOTONE CUBIC, not linearly. Straight segments
meet at an angle, so the ribbon visibly changes speed every time it
crosses a keyframe. Monotone cubic is smooth through them and, unlike
Catmull-Rom, cannot overshoot — a track never leaves the range its
stops describe.
3. The landing curve has ZERO SLOPE AT BOTH ENDS. A plate is on the rim
one frame and blending off it the next; if the blend starts at full
speed the plate visibly snaps off. See `settle`.
========================================================================== */
(function () {
'use strict';
var scene = document.querySelector('[data-wheel]');
if (!scene) return;
var pin = scene.querySelector('.scene__pin');
var plates = [].slice.call(scene.querySelectorAll('.plate'));
var lineA = scene.querySelector('.type__line--a');
var lineB = scene.querySelector('.type__line--b');
var rings = [].slice.call(scene.querySelectorAll('.rings__r'));
if (!pin || !plates.length) return;
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
/* ---- knobs ------------------------------------------------------------ *
Tracks are keyframes in scroll progress: [[p, value], ...]. W and H are
the pinned frame's width and height, so every number stays relative. */
var SPACING = 0.120; /* rad — the angle one plate subtends. Sets curvature. */
var ZT = 180; /* px — how far the rim leans toward the viewer */
var RYT = 9; /* deg — matching turn, which draws the trapezoid */
/* How hard the scene chases the scroll, per 60Hz frame. Higher is tighter
and steppier, lower is silkier and laggier; past about .25 the damping
stops doing anything useful, below about .08 the scene feels detached. */
var SMOOTH = 0.15;
var SETTLED = 0.0004; /* close enough to stop the loop */
var JUMP = 0.30; /* a bigger change than this is an anchor jump, not a
scroll — snap to it rather than gliding across */
/* A narrow frame needs bigger plates on a tighter arc, or the ribbon reads
as a thread. Both are raised together so the spacing stays seamless.
NARROW_W matches the breakpoint in css/style.css. */
var NARROW_W = 860;
var NARROW_FLY = 1.75;
var NARROW_SPACING = 0.22;
var LAND_AT = 0.46; /* the leading plate starts settling here */
var LAND_STEP = 0.020; /* each following plate waits this much longer */
var LAND_SPAN = 0.28; /* how long one plate takes to settle */
/* The lines START CENTRED and part. They used to start at -62%, which put
both of them most of the way off their own edges before anyone had
scrolled — so the first thing a reader saw was two sliced words either
side of an empty middle, and the entrance in the stylesheet was spending
itself sliding the headline into a place it could not be read.
At rest they are the hero's headline. Scrolling parts them, the plates
come up through the gap, and by TYPE_OUT they are gone. The price is the
crossing the reference had; the return is a first frame that is composed
rather than half-finished. */
var TYPE_OUT = 0.82;
var TYPE_FROM = 0;
var TYPE_TO = 112;
/* and they dim as they go, because the job changes: the headline at rest is
meant to be read, the same words behind ten flying plates are meant to be
a texture. DIM_BY is how far into the scene that handover is finished. */
var TYPE_DIM = 0.34;
var DIM_BY = 0.28;
/* plate width on screen, as a fraction of the frame's width */
var T_FLY = [[0, .085], [.20, .135], [.42, .185], [.64, .130], [1, .105]];
/* the top of the wheel, as a fraction of frame height (0 = frame top) */
var T_CREST = [[0, 1.12], [.20, .76], [.42, .44], [.64, .12], [1, -.06]];
/* the wheel's centre line, as a fraction of frame width */
var T_CX = [[0, .30], [.42, .50], [1, .62]];
/* spin, radians — where the ribbon sits around the rim */
var T_SPIN = [[0, -1.52], [.42, -.02], [.70, .52], [1, .76]];
/* ---- helpers ---------------------------------------------------------- */
function clamp(v, a, b) { return v < a ? a : v > b ? b : v; }
function lerp(a, b, t) { return a + (b - a) * t; }
/* The landing curve. easeOut alone gets a plate home fast — which is what
stops the frame emptying out mid-scene — but it leaves the rim at three
times its own speed, and that reads as a snap. Running the raw progress
through a smoothstep first makes the derivative zero at both ends while
keeping the arrival quick: half way through its window a plate is already
87% home. Smooth off the rim, smooth into the slot, still decisive. */
function smoothstep(x) { return x * x * (3 - 2 * x); }
function easeOut(x) { return 1 - Math.pow(1 - x, 3); }
function settle(x) { return easeOut(smoothstep(x)); }
/* ---- keyframe tracks, read with a monotone cubic ----------------------- *
Fritsch–Carlson: C1 continuous through every stop, and the clamped
tangents guarantee no segment overshoots the values its stops describe.
Tangents depend only on the stops, so they are computed once and cached
on the track itself. */
function prepare(stops) {
var n = stops.length, d = [], m = [], i;
for (i = 0; i < n - 1; i++) {
d.push((stops[i + 1][1] - stops[i][1]) / (stops[i + 1][0] - stops[i][0]));
}
m.push(d[0]);
for (i = 1; i < n - 1; i++) {
m.push(d[i - 1] * d[i] <= 0 ? 0 : (d[i - 1] + d[i]) / 2);
}
m.push(d[n - 2]);
for (i = 0; i < n - 1; i++) { /* clamp for monotonicity */
if (d[i] === 0) { m[i] = 0; m[i + 1] = 0; continue; }
var a = m[i] / d[i], b = m[i + 1] / d[i], s = a * a + b * b;
if (s > 9) {
var f = 3 / Math.sqrt(s);
m[i] = f * a * d[i];
m[i + 1] = f * b * d[i];
}
}
stops.m = m;
}
function track(stops, p) {
if (!stops.m) prepare(stops);
var n = stops.length;
if (p <= stops[0][0]) return stops[0][1];
if (p >= stops[n - 1][0]) return stops[n - 1][1];
var i = 0;
while (i < n - 2 && p > stops[i + 1][0]) i++;
var h = stops[i + 1][0] - stops[i][0];
var u = (p - stops[i][0]) / h, u2 = u * u, u3 = u2 * u;
return (2 * u3 - 3 * u2 + 1) * stops[i][1]
+ (u3 - 2 * u2 + u) * h * stops.m[i]
+ (-2 * u3 + 3 * u2) * stops[i + 1][1]
+ (u3 - u2) * h * stops.m[i + 1];
}
/* ---- one drawn frame -------------------------------------------------- */
var ringF = rings.map(function (el) {
return parseFloat(el.getAttribute('data-ring')) || 1;
});
var boxes = []; /* slot rect per plate, null where the layout dropped it */
var order = []; /* the plates in play this pass, in ribbon order */
var lastT = []; /* last transform written per plate */
var ringsLive = false;
/* Style writes, not reads, are what a pass costs — and most of them write the
value that is already there. Setting a custom property on the scene
invalidates its whole subtree, and once the plates have landed all ten are
handed the same `none` sixty times a second. Skipping the no-ops takes a
pass from ~1.8ms to well under half that, for nine lines. */
function setter(el) {
var last = {};
return function (name, v) {
if (last[name] === v) return;
last[name] = v;
el.style.setProperty(name, v);
};
}
var setScene = setter(scene);
var setA = lineA && setter(lineA);
var setB = lineB && setter(lineB);
function draw(p) {
/* ---- read phase --------------------------------------------------------
Every measurement first. Interleaving reads with style writes makes the
browser flush layout between each one; done in two phases the whole
frame costs a single layout. A slot the layout has dropped (the phone
grid hides the last four plates) has no box — collecting the live ones
here means the ribbon is centred on what is actually showing and the
landing order has no gaps. */
var pinBox = pin.getBoundingClientRect();
var W = pinBox.width, H = pinBox.height;
order.length = 0;
for (var k = 0; k < plates.length; k++) {
var b = plates[k].parentNode.getBoundingClientRect();
boxes[k] = b.width ? b : null;
if (b.width) order.push(k);
}
/* ---- write phase ---------------------------------------------------- */
/* -- the two display lines cross, one each way -- */
var tp = clamp(p / TYPE_OUT, 0, 1);
var dx = lerp(TYPE_FROM, TYPE_TO, tp) * W / 100;
if (setA) setA('--dx', dx.toFixed(1) + 'px');
if (setB) setB('--dx', (-dx).toFixed(1) + 'px');
setScene('--type-o', (1 - (1 - TYPE_DIM) * clamp(p / DIM_BY, 0, 1)).toFixed(3));
setScene('--rule-o', clamp((p - .76) / .14, 0, 1).toFixed(3));
/* The cue has done its job the moment anyone scrolls at all. It used to
go in 3% of the scene, which was right when it was a hairline at the
bottom edge; as a ring in the middle of the frame, vanishing that fast
reads as a glitch. 7% still clears it long before the plates get
there — they only enter the frame around p = 0.12. */
setScene('--cue-o', (1 - clamp(p / .07, 0, 1)).toFixed(3));
/* -- the wheel this pass -- */
var narrow = W < NARROW_W;
var spacing = narrow ? NARROW_SPACING : SPACING;
var flyW = track(T_FLY, p) * W * (narrow ? NARROW_FLY : 1);
var R = flyW / spacing;
var cx = track(T_CX, p) * W;
var crest = track(T_CREST, p) * H;
var spin = track(T_SPIN, p);
/* -- guide rings, drawn on the wheel's own centre -- */
var ringO = clamp(p / .10, 0, 1) * (1 - clamp((p - .46) / .16, 0, 1)) * .9;
if (ringO > 0 || ringsLive) { /* nothing to say once they are out */
ringsLive = ringO > 0;
for (var r = 0; r < rings.length; r++) {
var s = rings[r].style;
s.width = s.height = (2 * R * ringF[r]).toFixed(0) + 'px';
s.left = cx.toFixed(1) + 'px';
s.top = (crest + R).toFixed(1) + 'px';
s.opacity = ringO.toFixed(3);
}
}
for (k = 0; k < plates.length; k++) {
if (!boxes[k] && lastT[k] !== 'none') {
plates[k].style.transform = lastT[k] = 'none';
}
}
/* -- each plate: out to the rim, then eased back into its slot -- */
var mid = (order.length - 1) / 2;
for (var i = 0; i < order.length; i++) {
var n = order[i];
var plate = plates[n];
var box = boxes[n];
/* the leading plate lands first, then the rest in order */
var t = settle(clamp((p - LAND_AT - i * LAND_STEP) / LAND_SPAN, 0, 1));
if (t >= 1) {
if (lastT[n] !== 'none') plate.style.transform = lastT[n] = 'none';
continue;
}
var a = spin + (mid - i) * spacing;
var sn = Math.sin(a), cs = Math.cos(a);
/* rim position in viewport coordinates, then as a delta from the slot */
var rimX = pinBox.left + cx + R * sn;
var rimY = pinBox.top + crest + R * (1 - cs);
var tx = rimX - (box.left + box.width / 2);
var ty = rimY - (box.top + box.height / 2);
var tz = ZT * (1 - cs);
var rot = a * 180 / Math.PI;
var ry = RYT * sn;
var sc = flyW / box.width;
var next =
'translate3d(' + lerp(tx, 0, t).toFixed(2) + 'px,' +
lerp(ty, 0, t).toFixed(2) + 'px,' +
lerp(tz, 0, t).toFixed(2) + 'px) ' +
'rotate(' + lerp(rot, 0, t).toFixed(3) + 'deg) ' +
'rotateY(' + lerp(ry, 0, t).toFixed(3) + 'deg) ' +
'scale(' + lerp(sc, 1, t).toFixed(5) + ')';
if (lastT[n] !== next) plate.style.transform = lastT[n] = next;
}
}
/* ---- the damped follower ----------------------------------------------- *
`target` is where the scroll says we are; `cur` is where the scene is.
The loop runs only while the two disagree, so a parked page costs nothing.
The step is frame-rate corrected, so a 120Hz display glides at the same
speed as a 60Hz one rather than twice as fast. */
var target = 0, cur = 0, raf = 0, last = 0;
function measure() {
var travel = scene.offsetHeight - pin.offsetHeight;
if (travel <= 0) return false;
target = clamp(-scene.getBoundingClientRect().top / travel, 0, 1);
return true;
}
function frame(now) {
raf = 0;
var dt = last ? Math.min(now - last, 64) : 16.7;
last = now;
var gap = target - cur;
if (Math.abs(gap) < SETTLED) {
cur = target;
} else {
cur += gap * (1 - Math.pow(1 - SMOOTH, dt / 16.7));
raf = requestAnimationFrame(frame);
}
draw(cur);
}
function snap() {
if (!measure()) return;
cur = target;
if (raf) { cancelAnimationFrame(raf); raf = 0; }
draw(cur);
}
function onScroll() {
if (!measure()) return;
if (Math.abs(target - cur) > JUMP) return snap(); /* anchor jump, reload */
if (!raf) { last = 0; raf = requestAnimationFrame(frame); }
}
addEventListener('scroll', onScroll, { passive: true });
addEventListener('resize', snap, { passive: true }); /* no easing into a
new layout */
addEventListener('load', snap);
snap();
/* exposed for the page audit */
window.__wheel = {
plates: plates,
get spacing() { return SPACING; },
get progress() { return cur; },
tracks: { fly: T_FLY, crest: T_CREST, cx: T_CX, spin: T_SPIN },
track: track,
settle: settle,
draw: draw,
update: snap
};
})();
/* ============================================================================
3 · FIGURES
============================================================================ */
/* ============================================================================
Figures — a pinned scene that steps through four numbers.
The number and its label are two stacks in two one-line masks, and BOTH are
driven by the same `--fig-i`. One number, one shared index: the pair cannot
drift out of step, which is the failure two independently animated elements
fall into as soon as one transition is interrupted mid-slide.
---- one band each --------------------------------------------------------
Mapping progress straight to an index (`p * (N - 1)`) has two faults. The
stack is then in motion for the whole scene and never settles on a number,
which reads as drifting rather than as counting. And the first and last
numbers get a fraction of the dwell the middle ones do, because the ends
have only half a segment each — measured at 8% and 10% of the scene against
24% for the middles.
So progress is cut into N EQUAL bands, one per number. Inside its own band a
number holds, then slides away over the last `SLIDE` of it; the final one
holds for its whole band, having nowhere to slide to. Every number gets the
same 1/N of the scroll.
The slide uses the hero's landing ease — smoothstep into a cubic ease-out —
so it leaves and arrives at zero speed and the two scenes feel related.
---- the damper -----------------------------------------------------------
The same idea as js/wheel.js, and the long explanation lives there: the
scene follows a damped copy of the scroll, not the scroll itself, so a wheel
notch becomes a glide instead of a jump. Duplicated rather than shared
because this project has no build step and no modules, and fifteen lines is
cheaper than a dependency between two scenes.
========================================================================== */
(function () {
'use strict';
var scene = document.querySelector('[data-figures]');
if (!scene) return;
var pin = scene.querySelector('.figures__pin');
var bar = scene.querySelector('.figures__bar i');
var list = scene.querySelector('.figures__stack');
if (!pin || !list) return;
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var N = list.children.length;
if (N < 2) return;
/* ---- the two smoothness knobs, and which one actually matters ----------
SLIDE is how much of a number's own band is spent moving off it, so it
sets how GRADUAL a transition is per pixel of scroll. SMOOTH is the
damper, which is what turns a discrete scroll event into continuous
motion.
For a mouse wheel the damper is the one that matters, and by a distance.
A slide here spans roughly 270px of scroll on a 900px-tall viewport, so
one ~100px notch jumps a third of the way across it in a single event —
widening SLIDE cannot fix that, it only makes the jump a smaller fraction
of a longer slide. The damper is what stops it landing as a step: the
notch moves the TARGET, and the scene glides after it over ~500ms.
So SMOOTH is well below the hero's 0.15. The wheel scene moves
continuously and wants to track the scroll closely; this one is holding
still 75% of the time, so it can afford to trail much further behind
without ever reading as detached. */
var SLIDE = 0.46;
var SMOOTH = 0.09;
var SETTLED = 0.0004;
var JUMP = 0.30;
function clamp(v, a, b) { return v < a ? a : v > b ? b : v; }
function settle(x) {
var s = x * x * (3 - 2 * x); /* smoothstep */
return 1 - Math.pow(1 - s, 3); /* into a cubic ease-out */
}
var lastI = '', lastBar = '';
function draw(p) {
/* p -> a continuous index: N equal bands, each holding then sliding */
var seg = p * N;
var i = clamp(Math.floor(seg), 0, N - 1);
var t = clamp((seg - i - (1 - SLIDE)) / SLIDE, 0, 1);
var idx = clamp(i + settle(t), 0, N - 1).toFixed(4);
if (idx !== lastI) {
lastI = idx;
scene.style.setProperty('--fig-i', idx);
}
if (bar) {
var w = p.toFixed(4);
/* scaleY, because the rule runs down the right edge — see style.css */
if (w !== lastBar) { lastBar = w; bar.style.transform = 'scaleY(' + w + ')'; }
}
}
/* ---- the damped follower ------------------------------------------------ */
var target = 0, cur = 0, raf = 0, last = 0;
function measure() {
var travel = scene.offsetHeight - pin.offsetHeight;
if (travel <= 0) return false;
target = clamp(-scene.getBoundingClientRect().top / travel, 0, 1);
return true;
}
function frame(now) {
raf = 0;
var dt = last ? Math.min(now - last, 64) : 16.7;
last = now;
var gap = target - cur;
if (Math.abs(gap) < SETTLED) cur = target;
else {
cur += gap * (1 - Math.pow(1 - SMOOTH, dt / 16.7));
raf = requestAnimationFrame(frame);
}
draw(cur);
}
function snap() {
if (!measure()) return;
cur = target;
if (raf) { cancelAnimationFrame(raf); raf = 0; }
draw(cur);
}
function onScroll() {
if (!measure()) return;
if (Math.abs(target - cur) > JUMP) return snap();
if (!raf) { last = 0; raf = requestAnimationFrame(frame); }
}
addEventListener('scroll', onScroll, { passive: true });
addEventListener('resize', snap, { passive: true });
addEventListener('load', snap);
snap();
/* exposed for the page audit */
window.__figures = {
steps: N,
get progress() { return cur; },
get index() { return parseFloat(scene.style.getPropertyValue('--fig-i')) || 0; },
draw: draw,
update: snap
};
})();
/* ============================================================================
4 · REVEAL
============================================================================ */
/* ============================================================================
Section motion — reveals, counters, and parallax.
Generic on purpose: everything here keys off data attributes, so bringing
another section to life is a matter of marking up its elements, not writing
more JavaScript.
[data-reveal] fades and rises when it comes into view, and goes
back to its start state when it leaves. Stagger
with an inline --d delay, and give it a direction
with --rx / --ry.
[data-count="9"] counts up from zero when its [data-reveal] ancestor
fires. The real number lives in the markup, so it
is what shows with JS off.
[data-parallax] drifts against the scroll while its parent is on
screen. The element must have vertical slack to
move into — see .why__card--photo img.
[data-type] types itself out when revealed, ms per character.
The real text lives in the markup and is only
re-wrapped, so it is intact for a reader with JS
off and for a screen reader either way.
Reduced motion is handled once, at the top: everything is put in its final
state and no listener is bound. Nothing below it ever runs.
========================================================================== */
(function () {
'use strict';
var root = document.documentElement;
var reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
var targets = [].slice.call(document.querySelectorAll('[data-reveal]'));
/* ---- 1 · reveals ------------------------------------------------------- */
if (reduced || !('IntersectionObserver' in window)) {
targets.forEach(function (el) { el.classList.add('is-in'); });
return; /* no counters, no parallax */
}
/* ---- replay --------------------------------------------------------------
Scrolling back up returns a section to its start state, so scrolling down
again plays it again. Flip REPLAY to false for the other behaviour, where
a reveal happens once and then stays put — that is the only line that has
to change.
The hysteresis is what stops this flickering. An element is REVEALED once
20% of it is inside the viewport and RESET only once none of it is, so
there is a wide band between the two where nothing happens. One threshold
doing both jobs would toggle on every frame of a slow scroll that parked
an element exactly on the line. */
var REPLAY = true;
var shown = 0;
function show(el) {
if (el.classList.contains('is-in')) return;
el.classList.add('is-in');
shown++;
if (!REPLAY) io.unobserve(el);
fire(el);
}
function hide(el) {
if (!REPLAY || !el.classList.contains('is-in')) return;
el.classList.remove('is-in');
shown--;
reset(el);
}
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.intersectionRatio >= 0.2) show(en.target);
else if (!en.isIntersecting) hide(en.target);
});
}, { threshold: [0, 0.2], rootMargin: '0px 0px -6% 0px' });
/* ---- the safety net ------------------------------------------------------
A reveal's failure mode is content that is invisible forever, which is far
worse than content that simply appears. So there has to be a backstop —
but it has to be a backstop for the OBSERVER, not for the reader.
This was a blanket timer once: anything still hidden after 3s was shown,
wherever it happened to be on the page. That cannot tell "the observer
never fired" from "you have not scrolled there yet", and the second is
almost the whole document. Measured, on a cold load: three seconds in, all
37 reveals fired at once, still behind the loader — so by the time anyone
reached the why grid or the testimonials, every animation on the page had
already been spent. Nothing was broken. It had all happened where nobody
could see it.
So the sweep applies the observer's own two conditions, with the same gap
between them, and cannot disagree with the thing it is backing up. */
function sweep() {
queued = 0;
var vh = innerHeight, i, el, r;
for (i = targets.length - 1; i >= 0; i--) {
el = targets[i];
r = el.getBoundingClientRect();
if (r.top < vh * 0.9 && r.bottom > 0) show(el);
else if (r.bottom < -80 || r.top > vh + 80) hide(el);
}
}
/* Coalesced with setTimeout rather than rAF on purpose. The sweep is the
backstop for the observer, so it must not share a failure mode with it:
in an environment where the compositor is stalled, rAF and
IntersectionObserver stop together, and a net thrown by the same hand
that dropped the ball is not a net. setTimeout survives that.
80ms of latency costs nothing here — when the observer is alive it has
already revealed the element and the sweep finds nothing to do. */
var queued = 0;
function onSweep() {
if (queued) return;
queued = setTimeout(sweep, 80);
}
var started = false;
function start() {
if (started) return;
started = true;
targets.forEach(function (el) { io.observe(el); });
addEventListener('scroll', onSweep, { passive: true });
addEventListener('resize', onSweep, { passive: true });
sweep(); /* whatever is on screen now */
}
/* exposed for the page audit, the same way js/figures.js does it */
window.__reveal = {
total: targets.length,
replay: REPLAY,
get shown() { return shown; },
get started() { return started; },
sweep: sweep
};
/* Nothing may reveal while the loader is still over the page, or the first
screenful animates under an opaque panel and is over before anyone sees
it — the same waste as the old net, just smaller. The timeout is the
net's own net: js/boot.js caps itself at 5s, so if that file is missing
or wedged the reveals start anyway rather than never. */
if (root.classList.contains('hs-loading')) {
var mo = new MutationObserver(function () {
if (root.classList.contains('hs-loading')) return;
mo.disconnect();
start();
});
mo.observe(root, { attributes: true, attributeFilter: ['class'] });
setTimeout(function () { mo.disconnect(); start(); }, 6000);
} else {
start();
}
/* ---- what a revealed element carries ----------------------------------- */
function each(el, sel, fn) {
if (el.matches(sel)) fn(el);
[].slice.call(el.querySelectorAll(sel)).forEach(fn);
}
function fire(el) {
each(el, '[data-count]', count);
each(el, '[data-type]', typeOut);
}
/* Putting a section back means putting back what it contained. Without this
a counter that has already run to 9 fades out at 9 and fades back in at 9,
and only the box around it appears to replay. */
function reset(el) {
each(el, '[data-count]', function (c) {
var node = c.firstChild;
if (node && node.nodeType === 3) node.nodeValue = '0';
});
each(el, '[data-type]', function (t) {
delete t.dataset.typed;
[].slice.call(t.querySelectorAll('.tw-c')).forEach(function (c) {
c.classList.remove('is-on');
});
});
}
/* ---- 2 · counters ------------------------------------------------------ *
Writes into the first text node, so a trailing unit in its own element —
the "s" of "30s" — is left alone. */
function count(el) {
var to = parseFloat(el.getAttribute('data-count'));
var node = el.firstChild;
if (!(to > 0) || !node || node.nodeType !== 3) return;
var DUR = 950, t0 = 0, last = -1;
/* Scrolling past twice quickly would otherwise leave two rAF loops both
writing the same text node, and the number stutters between them. A
later run claims the element and the earlier one stops on its next
frame. */
var run = String(parseInt(el.dataset.run || '0', 10) + 1);
el.dataset.run = run;
requestAnimationFrame(function tick(now) {
if (el.dataset.run !== run) return;
if (!t0) t0 = now;
var t = Math.min(1, (now - t0) / DUR);
var v = Math.round(to * (1 - Math.pow(1 - t, 3))); /* ease out */
if (v !== last) { node.nodeValue = String(v); last = v; }
if (t < 1) requestAnimationFrame(tick);
});
}
/* ---- 3 · the typewriter ------------------------------------------------ *
Characters are revealed IN PLACE rather than appended. Appending grows the
text, and a heading that wraps to three lines re-breaks them on almost
every keystroke — the whole block jitters. Splitting up front means every
character holds its box from the first frame and only its ink arrives, so
the layout never moves.
The caret is zero-width, so parking it between two characters shifts
nothing; it is simply moved along and removed at the end. */
function typeOut(el) {
if (el.dataset.typed) return;
el.dataset.typed = '1';
var step = parseFloat(el.getAttribute('data-type')) || 26;
var cells = [].slice.call(el.querySelectorAll('.tw-c'));
/* On a replay the markup is already split, and splitting it again would
wrap every cell in another cell. */
if (cells.length) return type(el, cells, step);
var raw = el.textContent;
var frag = document.createDocumentFragment();
var i;
for (i = 0; i < raw.length; i++) {
var s = document.createElement('span');
s.className = 'tw-c';
s.textContent = raw[i];
frag.appendChild(s);
cells.push(s);
}
el.textContent = '';
el.appendChild(frag);
type(el, cells, step);
}
function type(el, cells, step) {
var caret = document.createElement('i');
caret.className = 'tw-caret';
caret.setAttribute('aria-hidden', 'true');
el.insertBefore(caret, cells[0]);
var mine = el.dataset.typed;
var n = 0;
(function tick() {
/* scrolled away mid-type, or a newer run has claimed the element */
if (el.dataset.typed !== mine) { caret.remove(); return; }
if (n >= cells.length) { caret.remove(); return; }
cells[n].classList.add('is-on');
n++;
if (n < cells.length) el.insertBefore(caret, cells[n]);
/* a space types instantly — pausing on one looks like a stall */
setTimeout(tick, cells[n - 1].textContent === ' ' ? 0 : step);
})();
}
/* ---- 4 · parallax ------------------------------------------------------ *
One rAF, one read phase, one write phase, and no write that repeats the
value already there — the same discipline as js/wheel.js. */
var drift = [].slice.call(document.querySelectorAll('[data-parallax]'));
if (!drift.length) return;
/* px either way, but never more than the slack the element actually has.
The picture is 122% tall pinned 11% high, so the room to move is 11% of
the card — and these cards range from 220px on a phone to 380px on a wide
desktop. A flat 26px walks an edge into frame at the small end. */
var AMP = 26, SLACK = 0.10;
var praf = 0, seen = [], written = [];
function pframe() {
praf = 0;
var vh = innerHeight, mid = vh / 2, i;
for (i = 0; i < drift.length; i++) { /* read */
var r = drift[i].parentNode.getBoundingClientRect();
seen[i] = (r.bottom < -150 || r.top > vh + 150)
? null /* off screen, skip it */
: [((r.top + r.height / 2) - mid) / vh, Math.min(AMP, r.height * SLACK)];
}
for (i = 0; i < drift.length; i++) { /* write */
if (seen[i] === null) continue;
var y = (-seen[i][0] * seen[i][1]).toFixed(1);
if (y === written[i]) continue;
written[i] = y;
drift[i].style.transform = 'translate3d(0,' + y + 'px,0)';
}
}
function onPScroll() { if (!praf) praf = requestAnimationFrame(pframe); }
addEventListener('scroll', onPScroll, { passive: true });
addEventListener('resize', onPScroll, { passive: true });
addEventListener('load', onPScroll);
pframe();
})();
/* ============================================================================
5 · FAQ
============================================================================ */
/* ============================================================================
FAQ — the accordion.
Deliberately not <details>. A closed <details> puts its content in
display:none, and nothing transitions out of display:none — so an animated
answer would have to be re-implemented around the element the moment you
wanted it to open smoothly. A button plus a region keeps the content in the
DOM at all times and lets CSS do the whole opening.
The height animation is CSS too: the panel is a one-row grid going 0fr to
1fr, which animates the real height of the text without JS ever measuring
it. That matters because the text reflows — a narrower window makes an
answer taller, and a measured pixel height would be stale the moment it did.
So all this file does is move one class and keep aria-expanded honest.
One at a time, because six open answers push the last question a screen and
a half down and the reader loses the list they were scanning.
========================================================================== */
(function () {
'use strict';
var list = document.querySelector('[data-faq]');
if (!list) return;
var items = [].slice.call(list.querySelectorAll('.faq__item'));
if (!items.length) return;
function set(item, on) {
item.classList.toggle('is-open', on);
var q = item.querySelector('.faq__q');
if (q) q.setAttribute('aria-expanded', on ? 'true' : 'false');
}
/* one listener on the list, so the markup can grow without touching this */
list.addEventListener('click', function (e) {
var btn = e.target.closest ? e.target.closest('.faq__q') : null;
if (!btn || !list.contains(btn)) return;
var item = btn.parentNode.parentNode; /* button < h3 < li */
var open = item.classList.contains('is-open');
items.forEach(function (other) { if (other !== item) set(other, false); });
set(item, !open);
});
/* Escape closes whatever is open, and leaves focus where it was */
list.addEventListener('keydown', function (e) {
if (e.key !== 'Escape') return;
items.forEach(function (it) { set(it, false); });
});
/* A link to a question opens it. The footer's Membership entry points at
#faq-q1, and landing on a closed question having asked for that exact one
is the sort of thing that reads as broken. Works for a pasted URL too,
which is the only reason the questions carry ids at all. */
function openFromHash() {
var id = location.hash.slice(1);
if (!id) return;
var q = document.getElementById(id);
if (!q || !list.contains(q)) return;
var item = q.closest ? q.closest('.faq__item') : null;
if (!item) return;
items.forEach(function (other) { if (other !== item) set(other, false); });
set(item, true);
}
addEventListener('hashchange', openFromHash);
openFromHash();
})();
})();
```
### B4 · section HTML (WP-media URLs, as stored)
```html
<div class="uichemy-halfsecond-1">
<div class="load" aria-hidden="true">
<p class="load__mark">Halfsecond</p>
<p class="load__num"><span data-load-num>00</span><small>%</small></p>
<div class="load__bar"><i data-load-bar></i></div>
</div>
<main id="top">
<section class="scene" data-wheel>
<div class="scene__pin">
<header class="nav">
<a class="nav__mark" href="#top">Halfsecond<i>Sports Centre</i></a>
<p class="nav__hours">Open 06:00–23:00</p>
<a class="nav__link" href="#timetable">Timetable</a>
</header>
<div class="rings" aria-hidden="true">
<span class="rings__r" data-ring="1"></span>
<span class="rings__r" data-ring="0.87"></span>
<span class="rings__r" data-ring="1.13"></span>
</div>
<div class="type" aria-hidden="true">
<p class="type__line type__line--a"><span>Every sport</span></p>
<p class="type__line type__line--b"><span>one place</span></p>
</div>
<h1 class="visually-hidden">Every sport, one place — courts, pitches and a track at Halfsecond Sports Centre.</h1>
<div class="work">
<ul class="work__grid">
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-01-lane.webp" alt="A sprinter driving down lane four on an outdoor track" width="900" height="1125" fetchpriority="high" decoding="async">
<b class="plate__bib">01</b>
<figcaption>
<strong>Track Nights</strong>
<span>Athletics<i>£6</i></span>
</figcaption>
<a class="plate__go" href="#session-01"><span class="visually-hidden">Track Nights — find it in the timetable</span></a>
</figure>
</li>
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-02-corner.webp" alt="Two hockey players meeting over the ball in a cloud of dust" width="900" height="1125" decoding="async">
<b class="plate__bib">02</b>
<figcaption>
<strong>Astro Pitch</strong>
<span>Hockey<i>£14</i></span>
</figcaption>
<a class="plate__go" href="#session-02"><span class="visually-hidden">Astro Pitch — find it in the timetable</span></a>
</figure>
</li>
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-03-rim.webp" alt="A player rising to the rim against an open sky" width="900" height="1125" decoding="async">
<b class="plate__bib">03</b>
<figcaption>
<strong>Open Court</strong>
<span>Basketball<i>£8</i></span>
</figcaption>
<a class="plate__go" href="#session-03"><span class="visually-hidden">Open Court — find it in the timetable</span></a>
</figure>
</li>
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-04-post.webp" alt="A player turning in the post, lit hard against red" width="900" height="1125" decoding="async">
<b class="plate__bib">04</b>
<figcaption>
<strong>League Night</strong>
<span>Basketball<i>£10</i></span>
</figcaption>
<a class="plate__go" href="#session-04"><span class="visually-hidden">League Night — find it in the timetable</span></a>
</figure>
</li>
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-05-crosscourt.webp" alt="A padel player meeting the ball at the net" width="900" height="1125" decoding="async">
<b class="plate__bib">05</b>
<figcaption>
<strong>Padel Courts</strong>
<span>Padel<i>£12</i></span>
</figcaption>
<a class="plate__go" href="#session-05"><span class="visually-hidden">Padel Courts — find it in the timetable</span></a>
</figure>
</li>
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-06-fiveaside.webp" alt="A five-a-side player breaking away under floodlights" width="900" height="1125" decoding="async">
<b class="plate__bib">06</b>
<figcaption>
<strong>Five-a-side</strong>
<span>Indoor<i>£9</i></span>
</figcaption>
<a class="plate__go" href="#session-06"><span class="visually-hidden">Five-a-side — find it in the timetable</span></a>
</figure>
</li>
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-07-strike.webp" alt="A striker connecting with the ball in front of a full stand" width="900" height="1125" decoding="async">
<b class="plate__bib">07</b>
<figcaption>
<strong>Match Pitch</strong>
<span>Football<i>£16</i></span>
</figcaption>
<a class="plate__go" href="#session-07"><span class="visually-hidden">Match Pitch — find it in the timetable</span></a>
</figure>
</li>
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-08-clay.webp" alt="A tennis player driving through a shot on clay" width="900" height="1125" decoding="async">
<b class="plate__bib">08</b>
<figcaption>
<strong>Clay Courts</strong>
<span>Tennis<i>£11</i></span>
</figcaption>
<a class="plate__go" href="#session-08"><span class="visually-hidden">Clay Courts — find it in the timetable</span></a>
</figure>
</li>
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-09-count.webp" alt="A batter making contact, dirt kicked up behind the plate" width="900" height="1125" decoding="async">
<b class="plate__bib">09</b>
<figcaption>
<strong>The Cage</strong>
<span>Baseball<i>£7</i></span>
</figcaption>
<a class="plate__go" href="#session-09"><span class="visually-hidden">The Cage — find it in the timetable</span></a>
</figure>
</li>
<li class="slot">
<figure class="plate">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-10-notout.webp" alt="A cricketer raising both arms after reaching a century" width="900" height="1125" decoding="async">
<b class="plate__bib">10</b>
<figcaption>
<strong>Cricket Nets</strong>
<span>Cricket<i>£7</i></span>
</figcaption>
<a class="plate__go" href="#session-10"><span class="visually-hidden">Cricket Nets — find it in the timetable</span></a>
</figure>
</li>
</ul>
</div>
<p class="note">No membership needed to book — turn up, pay for the hour, play. Members get priority booking and the gym.</p>
<div class="scrollcue" aria-hidden="true">
<svg class="scrollcue__ring" viewbox="0 0 100 100">
<defs>
<path id="cuearc" fill="none" d="M50,50 m-36,0 a36,36 0 1,1 72,0 a36,36 0 1,1 -72,0"></path>
</defs>
<circle class="scrollcue__edge" cx="50" cy="50" r="46"></circle>
<text class="scrollcue__text">
<textpath href="#cuearc" textlength="226" lengthadjust="spacing">Scroll to explore · Nine sports, ten sessions · </textpath>
</text>
</svg>
<i class="scrollcue__arrow"></i>
</div>
<div class="cta">
<a class="cta__go" href="#timetable">Book a court<i>→</i></a>
<p class="cta__note">Membership from £38 a month</p>
</div>
</div>
</section>
<section class="why" id="why">
<p class="why__eyebrow" data-reveal>Why here</p>
<h2 class="why__head" data-reveal data-type="30" style="--d:.06s">Why people play at Halfsecond</h2>
<div class="why__grid">
<article class="why__card why__card--wide why__card--photo" data-reveal style="--d:.30s;--rx:-70px">
<img data-parallax src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-02-corner.webp" alt="" width="900" height="1125" decoding="async">
<div class="why__body">
<h3>Match-standard surfaces</h3>
<p>Sanded astro, sprung indoor courts and a six-lane track — the
same surfaces you would play a fixture on.</p>
</div>
</article>
<article class="why__card why__card--narrow why__card--lime" data-reveal style="--d:.38s;--rx:70px">
<p class="why__stat" data-count="9">9</p>
<div class="why__body">
<h3>Sports, one building</h3>
<p>Change once and play anything. No second venue, no second membership.</p>
</div>
</article>
<article class="why__card why__card--narrow why__card--flat" data-reveal style="--d:.46s;--rx:-70px">
<p class="why__stat" data-count="30">30<small>s</small></p>
<div class="why__body">
<h3>Booked in half a minute</h3>
<p>Live availability, pay for the hour, cancel free up to two hours before.</p>
</div>
</article>
<article class="why__card why__card--wide why__card--photo" data-reveal style="--d:.54s;--rx:70px">
<img data-parallax src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-06-fiveaside.webp" alt="" width="900" height="1125" decoding="async">
<div class="why__body">
<h3>Open 06:00 to 23:00</h3>
<p>Before work, after work, or the middle of a Tuesday. Seventeen hours
a day, every day of the year.</p>
</div>
</article>
</div>
</section>
<section class="figures" id="figures" data-figures>
<div class="figures__pin">
<p class="figures__eyebrow">Halfsecond in numbers</p>
<div class="figures__num">
<ul class="figures__stack">
<li>14,000<small>+</small></li>
<li>22</li>
<li>340</li>
<li>17</li>
</ul>
</div>
<div class="figures__lab">
<ul class="figures__stack">
<li>Court hours booked a year</li>
<li>Courts, pitches and lanes</li>
<li>Teams in our leagues</li>
<li>Hours open, every day</li>
</ul>
</div>
<div class="figures__bar" aria-hidden="true"><i></i></div>
</div>
</section>
<section class="ledger" id="timetable">
<h2 class="ledger__title" data-reveal>What’s on,<br>and what it costs</h2>
<ol class="ledger__list">
<li id="session-01" data-reveal style="--d:0.000s"><b>Track Nights</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-01-lane.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Athletics · Tue & Thu, 6–9pm</span><i>£6</i></li>
<li id="session-02" data-reveal style="--d:0.025s"><b>Astro Pitch</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-02-corner.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Hockey · Sat, 9am–1pm</span><i>£14</i></li>
<li id="session-03" data-reveal style="--d:0.050s"><b>Open Court</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-03-rim.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Basketball · daily, drop-in</span><i>£8</i></li>
<li id="session-04" data-reveal style="--d:0.075s"><b>League Night</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-04-post.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Basketball · Mon, 8–10pm</span><i>£10</i></li>
<li id="session-05" data-reveal style="--d:0.100s"><b>Padel Courts</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-05-crosscourt.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Padel · daily, by the hour</span><i>£12</i></li>
<li id="session-06" data-reveal style="--d:0.125s"><b>Five-a-side</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-06-fiveaside.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Indoor football · Wed & Fri, 7–10pm</span><i>£9</i></li>
<li id="session-07" data-reveal style="--d:0.150s"><b>Match Pitch</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-07-strike.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Football · Sun, by arrangement</span><i>£16</i></li>
<li id="session-08" data-reveal style="--d:0.175s"><b>Clay Courts</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-08-clay.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Tennis · Apr–Sep, daily</span><i>£11</i></li>
<li id="session-09" data-reveal style="--d:0.200s"><b>The Cage</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-09-count.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Baseball · Sat & Sun</span><i>£7</i></li>
<li id="session-10" data-reveal style="--d:0.225s"><b>Cricket Nets</b><img class="ledger__peek" src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-10-notout.webp" alt="" width="900" height="1125" loading="lazy" decoding="async"><span>Cricket · Apr–Sep, daily</span><i>£7</i></li>
</ol>
<div class="join" data-reveal style="--d:.06s">
<p class="join__lead">Membership <b>£38</b> a month</p>
<p class="join__body">Priority booking seven days ahead, gym and track included,
20% off every court and pitch. No joining fee, cancel any month.</p>
<a class="cta__go" href="#join">Join Halfsecond<i>→</i></a>
</div>
</section>
<section class="voices" id="members">
<p class="voices__eyebrow" data-reveal>Members</p>
<h2 class="voices__head" data-reveal style="--d:.06s">Booked once.<br>Back every week.</h2>
<ul class="voices__list">
<li class="voices__item" data-reveal style="--d:.06s">
<figure class="voices__shot">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-05-crosscourt.webp" alt="A padel player meeting the ball at the net" width="900" height="1125" loading="lazy" decoding="async">
<b class="voices__bib">01</b>
</figure>
<blockquote class="voices__say">
<p>“Booked a court on the way home and played an hour later.”</p>
<footer class="voices__who">Priya<a href="#session-05">Padel</a></footer>
</blockquote>
</li>
<li class="voices__item" data-reveal style="--d:.06s">
<figure class="voices__shot">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-01-lane.webp" alt="A sprinter on the outdoor track" width="900" height="1125" loading="lazy" decoding="async">
<b class="voices__bib">02</b>
</figure>
<blockquote class="voices__say">
<p>“Nobody minds that I am slow. That was the thing I worried about.”</p>
<footer class="voices__who">Ellie<a href="#session-01">Athletics</a></footer>
</blockquote>
</li>
<li class="voices__item" data-reveal style="--d:.06s">
<figure class="voices__shot">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-08-clay.webp" alt="A tennis player driving through a shot on clay" width="900" height="1125" loading="lazy" decoding="async">
<b class="voices__bib">03</b>
</figure>
<blockquote class="voices__say">
<p>“One membership, and my son plays cricket while I am on a court.”</p>
<footer class="voices__who">Raj<a href="#session-08">Tennis</a></footer>
</blockquote>
</li>
<li class="voices__item" data-reveal style="--d:.06s">
<figure class="voices__shot">
<img src="https://demo.uichemy.com/wp-content/uploads/2026/09/sp-07-strike.webp" alt="A striker connecting with the ball" width="900" height="1125" loading="lazy" decoding="async">
<b class="voices__bib">04</b>
</figure>
<blockquote class="voices__say">
<p>“Six of us split a pitch on a Sunday. Cheaper than a round.”</p>
<footer class="voices__who">Tom<a href="#session-07">Football</a></footer>
</blockquote>
</li>
</ul>
</section>
<section class="faq" id="faq">
<div class="faq__head">
<p class="faq__eyebrow" data-reveal>Before you come</p>
<h2 class="faq__title" data-reveal style="--d:.06s">Asked at<br>the desk</h2>
<p class="faq__note" data-reveal style="--d:.12s">
Not here? Somebody picks up from six in the morning —
<a href="tel:+441614960042">0161 496 0042</a>.
</p>
</div>
<ul class="faq__list" data-faq>
<li class="faq__item is-open" data-reveal>
<h3 class="faq__row"><button class="faq__q" type="button" id="faq-q1" aria-controls="faq-a1" aria-expanded="true">
<span>Do I need to be a member?</span><i class="faq__mark" aria-hidden="true"></i>
</button></h3>
<div class="faq__a" id="faq-a1" role="region" aria-labelledby="faq-q1"><div>
<p>No. Everything on the timetable is drop-in — turn up, pay for the session, play. Membership starts to pay for itself at about two visits a week, and the desk will say so when you get there rather than sell it to you at the door.</p>
</div></div>
</li>
<li class="faq__item" data-reveal style="--d:.04s">
<h3 class="faq__row"><button class="faq__q" type="button" id="faq-q2" aria-controls="faq-a2" aria-expanded="false">
<span>How far ahead can I book a court?</span><i class="faq__mark" aria-hidden="true"></i>
</button></h3>
<div class="faq__a" id="faq-a2" role="region" aria-labelledby="faq-q2"><div>
<p>Fourteen days. Weekday evenings between six and eight go within a day of opening, so set a reminder if that is your slot. Track nights and open court are never booked at all — just come.</p>
</div></div>
</li>
<li class="faq__item" data-reveal style="--d:.08s">
<h3 class="faq__row"><button class="faq__q" type="button" id="faq-q3" aria-controls="faq-a3" aria-expanded="false">
<span>I have not played since school</span><i class="faq__mark" aria-hidden="true"></i>
</button></h3>
<div class="faq__a" id="faq-a3" role="region" aria-labelledby="faq-q3"><div>
<p>Neither had half the people at open court. Every session on the list has a coach on the floor, not just the beginner ones. Say where you are at the desk and you will be put on with people going at your pace.</p>
</div></div>
</li>
<li class="faq__item" data-reveal style="--d:.12s">
<h3 class="faq__row"><button class="faq__q" type="button" id="faq-q4" aria-controls="faq-a4" aria-expanded="false">
<span>Do I need my own kit?</span><i class="faq__mark" aria-hidden="true"></i>
</button></h3>
<div class="faq__a" id="faq-a4" role="region" aria-labelledby="faq-q4"><div>
<p>Rackets, bibs and balls are at the desk, free, for padel, tennis and five-a-side. Bring trainers with a clean sole — the indoor courts are the one thing we are strict about, and the desk will turn you back for it.</p>
</div></div>
</li>
<li class="faq__item" data-reveal style="--d:.16s">
<h3 class="faq__row"><button class="faq__q" type="button" id="faq-q5" aria-controls="faq-a5" aria-expanded="false">
<span>Can I cancel a booking?</span><i class="faq__mark" aria-hidden="true"></i>
</button></h3>
<div class="faq__a" id="faq-a5" role="region" aria-labelledby="faq-q5"><div>
<p>Up to four hours before, free, by phone or online. After that the slot is charged, because by then nobody else can take it. Ring anyway if something has genuinely gone wrong.</p>
</div></div>
</li>
<li class="faq__item" data-reveal style="--d:.2s">
<h3 class="faq__row"><button class="faq__q" type="button" id="faq-q6" aria-controls="faq-a6" aria-expanded="false">
<span>Where do I park?</span><i class="faq__mark" aria-hidden="true"></i>
</button></h3>
<div class="faq__a" id="faq-a6" role="region" aria-labelledby="faq-q6"><div>
<p>Twenty spaces off Prospect Street, free while you are on site. They go on league nights — the Ancoats multi-storey is four minutes on foot and the desk will stamp your ticket.</p>
</div></div>
</li>
</ul>
</section>
</main>
<footer class="foot">
<img class="foot__bg" src="https://demo.uichemy.com/wp-content/uploads/2026/09/foot-track.webp" alt="" width="160" height="60" decoding="async">
<div class="foot__panel">
<div class="foot__top">
<div class="foot__ask" id="join" data-reveal>
<h2 class="foot__head">Join Halfsecond and get on a court this week</h2>
<a class="cta__go" href="#timetable">Join now<i>→</i></a>
</div>
<nav class="foot__col foot__col--a" aria-label="Explore" data-reveal style="--d:.09s">
<h3>Explore</h3>
<ul>
<li><a href="#top">Sports</a></li>
<li><a href="#timetable">Timetable</a></li>
<li><a href="#faq-q1">Membership</a></li>
<li><a href="#faq">Questions</a></li>
<li><a href="#why">Coaching</a></li>
</ul>
</nav>
<div class="foot__col foot__col--b" data-reveal style="--d:.16s">
<h3>Visit</h3>
<ul>
<li>Unit 4, Prospect Works</li>
<li>Ancoats, Manchester M4 6DE</li>
<li><a href="tel:+441614960042">0161 496 0042</a></li>
<li>Open 06:00–23:00, daily</li>
</ul>
</div>
</div>
<div class="foot__base" data-reveal style="--d:.22s">
<p class="foot__mark">Halfsecond</p>
<p class="foot__legal">
<a href="#top">Privacy</a>
<a href="#top">Terms</a>
</p>
</div>
</div>
</footer>
</div>
```
### B5 · section CSS (as sent)
```css
.uichemy-halfsecond-1 {
--bg: #0C0D0F;
--bg-deep: #14161A;
--ink: #F4F5F3;
--ink-mute: #8B9096;
--hair: rgba(244,245,243,.16);
--accent: #D4FF2B;
--accent-ink: var(--accent);
--font-display: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
--font-ui: 'Barlow Condensed', 'Helvetica Neue', Arial, sans-serif;
--gutter: clamp(18px, 3.2vw, 48px);
--ease: cubic-bezier(.22,.61,.36,1);
--grid-cols: 5;
--grid-gap: clamp(7px, .8vw, 13px);
--row-gap: calc(var(--grid-gap) * 1.9);
--grid-h: min(72svh, 860px);
--row-h: calc((var(--grid-h) - var(--row-gap)) / 2);
--grid-w: min(1560px, 92vw,
calc(var(--row-h) * 1.05 * var(--grid-cols)
+ var(--grid-gap) * (var(--grid-cols) - 1)));
}
.uichemy-halfsecond-1 *,
.uichemy-halfsecond-1 *::before,
.uichemy-halfsecond-1 *::after {
box-sizing: border-box;
}
.uichemy-halfsecond-1 :is(main[id], section[id], .ledger__list li[id], .foot__ask, .faq__q) {
scroll-margin-top: clamp(20px, 8vh, 80px);
}
.uichemy-halfsecond-1 :where(a:focus-visible),
.uichemy-halfsecond-1 :where(button:focus-visible) {
outline: 2px solid currentColor; outline-offset: 3px;
}
.uichemy-halfsecond-1 {
background: var(--bg);
color: var(--ink);
font: 500 16px/1.45 var(--font-ui);
-webkit-font-smoothing: antialiased;
}
.uichemy-halfsecond-1 :where(img) {
display: block; max-width: 100%;
}
.uichemy-halfsecond-1 .visually-hidden {
position: absolute; width: 1px; height: 1px; margin: -1px;
padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.uichemy-halfsecond-1 .nav {
position: absolute; inset: 0 0 auto 0; z-index: 40;
display: flex; align-items: baseline; justify-content: space-between;
gap: clamp(14px, 2.4vw, 34px);
padding: clamp(15px, 2.2vw, 24px) var(--gutter);
pointer-events: none;
}
.uichemy-halfsecond-1 .nav a {
pointer-events: auto;
}
.uichemy-halfsecond-1 .nav__mark {
display: flex; align-items: baseline; gap: .7em;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(15px, 1.35vw, 20px);
letter-spacing: -.01em; text-transform: uppercase;
color: var(--ink); text-decoration: none;
}
.uichemy-halfsecond-1 .nav__mark i {
font: 500 clamp(11px, .82vw, 12px)/1 var(--font-ui);
font-style: normal;
letter-spacing: .2em; text-transform: uppercase;
color: var(--ink-mute);
}
.uichemy-halfsecond-1 .nav__hours {
margin: 0;
font: 500 clamp(11px, .82vw, 12px)/1 var(--font-ui);
letter-spacing: .2em; text-transform: uppercase;
color: var(--accent);
}
.uichemy-halfsecond-1 .nav__link {
font: 500 clamp(11px, .82vw, 12px)/1 var(--font-ui);
letter-spacing: .2em; text-transform: uppercase;
color: var(--ink-mute); text-decoration: none;
padding-bottom: 4px; border-bottom: 1px solid var(--hair);
transition: color .4s var(--ease), border-color .4s var(--ease);
}
.uichemy-halfsecond-1 .nav__link:hover {
color: var(--accent); border-color: var(--accent);
}
.uichemy-halfsecond-1 .scene {
position: relative; height: 520svh;
}
.uichemy-halfsecond-1 .scene__pin {
position: sticky; top: 0;
height: 100vh;
height: 100svh;
overflow: hidden;
perspective: 1600px;
perspective-origin: 50% 46%;
}
.uichemy-halfsecond-1 .rings {
position: absolute; inset: 0; pointer-events: none;
}
.uichemy-halfsecond-1 .rings__r {
position: absolute; left: 0; top: 0;
width: 1px; height: 1px;
border: 1px solid rgba(244,245,243,.11);
border-radius: 50%;
transform: translate(-50%, -50%);
opacity: 0;
}
.uichemy-halfsecond-1 .type {
position: absolute; inset: 0; pointer-events: none;
}
.uichemy-halfsecond-1 .type__line {
position: absolute; left: 50%; width: max-content; margin: 0;
font-family: var(--font-display);
font-weight: 800;
font-stretch: 82%;
font-style: italic;
font-size: clamp(62px, min(10.4vw, 18vh), 220px);
line-height: .86;
letter-spacing: -.035em;
text-transform: uppercase;
--type-c: color-mix(in srgb, var(--ink) 86%, var(--bg));
color: var(--type-c);
opacity: var(--type-o, 1);
white-space: nowrap;
transform: translate(calc(-50% + var(--dx, 0px)), -50%);
will-change: transform, opacity;
}
.uichemy-halfsecond-1 .type__line--a {
top: 31%;
}
.uichemy-halfsecond-1 .type__line--b {
top: 64%;
}
.uichemy-halfsecond-1 @supports (-webkit-text-stroke: 1px currentColor) {
.type__line--b {
color: transparent;
-webkit-text-stroke: .022em var(--type-c);
}
}
.uichemy-halfsecond-1 .note {
position: absolute; left: var(--gutter); bottom: clamp(24px, 4.4vh, 52px);
margin: 0; max-width: 30ch;
font: 400 clamp(11.5px, 1vw, 14px)/1.5 var(--font-ui);
color: var(--ink-mute);
}
.uichemy-halfsecond-1 .cta {
position: absolute; right: var(--gutter); bottom: clamp(22px, 4vh, 48px);
display: flex; flex-direction: column; align-items: flex-end; gap: .7em;
}
.uichemy-halfsecond-1 .cta__go {
display: inline-flex; align-items: center; gap: 1.1em;
padding: .95em 1.5em;
background: var(--accent); color: #0C0D0F;
font: 600 clamp(11.5px, .92vw, 14px)/1 var(--font-ui);
letter-spacing: .16em; text-transform: uppercase; text-decoration: none;
transition: transform .35s var(--ease), filter .35s var(--ease);
}
.uichemy-halfsecond-1 .cta__go i {
font-style: normal; transition: transform .35s var(--ease);
}
.uichemy-halfsecond-1 .cta__go:hover {
transform: translateY(-2px); filter: brightness(1.08);
}
.uichemy-halfsecond-1 .cta__go:hover i {
transform: translateX(4px);
}
.uichemy-halfsecond-1 .cta__note {
margin: 0;
font: 400 clamp(11px, .9vw, 13px)/1 var(--font-ui);
letter-spacing: .14em; text-transform: uppercase;
color: var(--ink-mute);
}
.uichemy-halfsecond-1 .work {
position: absolute; left: 50%; top: 44%;
transform: translate(-50%, -50%);
width: var(--grid-w);
}
.uichemy-halfsecond-1 .work__grid {
position: relative;
list-style: none; margin: 0; padding: 0;
display: grid;
grid-template-columns: repeat(var(--grid-cols), 1fr);
gap: var(--row-gap) var(--grid-gap);
}
.uichemy-halfsecond-1 .work__grid::before {
content: ""; position: absolute;
left: -4vw; right: -4vw;
top: calc(var(--row-h) + var(--row-gap) / 2);
border-top: 1px solid var(--hair);
opacity: var(--rule-o, 1);
pointer-events: none;
}
.uichemy-halfsecond-1 .slot {
position: relative;
height: var(--row-h);
container-type: inline-size;
}
.uichemy-halfsecond-1 .plate {
position: absolute; inset: 0; margin: 0;
overflow: hidden;
background: #17181B;
color: rgb(255, 255, 255);
transform-origin: 50% 50%;
backface-visibility: hidden;
}
.uichemy-halfsecond-1 .plate img {
width: 100%; height: 100%; object-fit: cover;
}
.uichemy-halfsecond-1 .plate__go {
position: absolute; inset: 0; z-index: 3;
transition: box-shadow .25s var(--ease);
}
.uichemy-halfsecond-1 .plate__go:hover {
box-shadow: inset 0 0 0 2px var(--accent);
}
.uichemy-halfsecond-1 .plate__go:focus-visible {
outline: none; box-shadow: inset 0 0 0 3px var(--accent);
}
.uichemy-halfsecond-1 .plate::after {
content: ""; position: absolute; inset: auto 0 0 0; height: 46%;
background: linear-gradient(to top, rgba(6,7,9,.68), rgba(6,7,9,0));
opacity: 0;
transition: opacity .38s var(--ease);
}
.uichemy-halfsecond-1 .plate__bib {
position: absolute; left: 7%; top: 5%; z-index: 2;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(13px, 10cqw, 38px);
line-height: 1; letter-spacing: -.02em;
font-variant-numeric: tabular-nums;
opacity: .82;
}
.uichemy-halfsecond-1 .plate figcaption {
position: absolute; left: 7%; right: 7%; bottom: 6%; z-index: 2;
display: flex; flex-direction: column; gap: .3em;
font-family: var(--font-ui);
opacity: 0;
transform: translateY(9px);
transition: opacity .38s var(--ease), transform .5s var(--ease);
}
.uichemy-halfsecond-1 .plate:hover figcaption,
.uichemy-halfsecond-1 .plate:focus-within figcaption {
opacity: 1; transform: none;
}
.uichemy-halfsecond-1 .plate:hover::after,
.uichemy-halfsecond-1 .plate:focus-within::after {
opacity: 1;
}
.uichemy-halfsecond-1 .plate figcaption strong {
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(12px, 10.5cqw, 34px);
letter-spacing: -.025em; line-height: 1;
text-transform: uppercase;
}
.uichemy-halfsecond-1 .plate figcaption span {
display: flex; align-items: baseline; justify-content: space-between; gap: 1em;
font-size: clamp(10.5px, 5cqw, 14px);
font-weight: 500; letter-spacing: .13em; text-transform: uppercase;
color: rgba(255,255,255,.72);
}
.uichemy-halfsecond-1 .plate figcaption i {
font-style: normal;
font-variant-numeric: tabular-nums;
letter-spacing: .04em;
color: var(--accent);
white-space: nowrap;
}
.uichemy-halfsecond-1 .plate--light {
color: #0C0D0F;
}
.uichemy-halfsecond-1 .plate--light::after {
background: linear-gradient(to top, rgba(246,246,242,.80), rgba(246,246,242,0));
}
.uichemy-halfsecond-1 .plate--light figcaption span {
color: rgba(12,13,15,.66);
}
.uichemy-halfsecond-1 .plate--light figcaption i {
color: #0C0D0F;
}
.uichemy-halfsecond-1 .plate--light .plate__bib {
opacity: .7;
}
.uichemy-halfsecond-1 .why {
position: relative; z-index: 2;
overflow-x: clip;
background: var(--bg);
padding: clamp(60px, 10vh, 132px) var(--gutter) clamp(48px, 8vh, 104px);
}
.uichemy-halfsecond-1 .why__eyebrow,
.uichemy-halfsecond-1 .figures__eyebrow,
.uichemy-halfsecond-1 .voices__eyebrow {
margin: 0 0 clamp(14px, 1.8vh, 24px);
font: 600 clamp(11px, .95vw, 14px)/1 var(--font-ui);
letter-spacing: .22em; text-transform: uppercase;
color: var(--accent);
}
.uichemy-halfsecond-1 .why__head {
margin: 0 0 clamp(28px, 5vh, 58px);
max-width: 13ch;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(34px, 5.4vw, 88px);
line-height: .94; letter-spacing: -.035em;
text-transform: uppercase;
color: var(--ink);
}
.uichemy-halfsecond-1 .why__grid {
display: grid;
grid-template-columns: repeat(10, minmax(0, 1fr));
gap: clamp(8px, .9vw, 14px);
}
.uichemy-halfsecond-1 .why__card--wide {
grid-column: span 7;
}
.uichemy-halfsecond-1 .why__card--narrow {
grid-column: span 3;
}
.uichemy-halfsecond-1 .why__card {
position: relative; isolation: isolate; overflow: hidden;
display: flex; flex-direction: column; justify-content: flex-end;
min-height: clamp(225px, 25vw, 380px);
padding: clamp(18px, 1.9vw, 30px);
border-radius: clamp(10px, 1vw, 14px);
}
.uichemy-halfsecond-1 .why__card--photo {
color: rgb(255, 255, 255);
}
.uichemy-halfsecond-1 .why__card--photo img {
position: absolute; inset: 0; z-index: -2;
width: 100%; height: 100%; object-fit: cover;
}
.uichemy-halfsecond-1 .why__card--photo::after {
content: ""; position: absolute; inset: 0; z-index: -1;
background: linear-gradient(transparent 28%, rgba(6,7,9,.86));
}
.uichemy-halfsecond-1 .why__card--lime {
background: var(--accent); color: #0C0D0F;
}
.uichemy-halfsecond-1 .why__card--flat {
background: #14161A; border: 1px solid var(--hair); color: var(--ink);
}
.uichemy-halfsecond-1 .why__stat {
margin: 0 auto auto 0;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(50px, 6.4vw, 104px);
line-height: .8; letter-spacing: -.05em;
font-variant-numeric: tabular-nums;
}
.uichemy-halfsecond-1 .why__stat small {
font-size: .42em; letter-spacing: -.02em;
}
.uichemy-halfsecond-1 .why__card--flat .why__stat {
color: var(--accent);
}
.uichemy-halfsecond-1 .why__body h3 {
margin: 0 0 .5em;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(17px, 1.7vw, 27px);
line-height: 1.02; letter-spacing: -.025em;
text-transform: uppercase;
}
.uichemy-halfsecond-1 .why__body p {
margin: 0; max-width: 46ch;
font: 400 clamp(12.5px, 1.05vw, 15.5px)/1.45 var(--font-ui);
color: rgba(255,255,255,.7);
}
.uichemy-halfsecond-1 .why__card--lime .why__body p {
color: rgba(12,13,15,.72);
}
.uichemy-halfsecond-1 .why__card--flat .why__body p {
color: var(--ink-mute);
}
.uichemy-halfsecond-1 .why__card--photo img {
top: -11%; bottom: auto; left: 0; right: 0;
width: 100%; height: 122%;
object-fit: cover;
will-change: transform;
}
.uichemy-halfsecond-1 .why__card--photo img {
scale: 1; transition: scale .7s var(--ease);
}
.uichemy-halfsecond-1 .why__card--photo:hover img {
scale: 1.045;
}
.uichemy-halfsecond-1 .why__card--lime,
.uichemy-halfsecond-1 .why__card--flat {
transition: transform .45s var(--ease), border-color .45s var(--ease);
}
.uichemy-halfsecond-1 .why__card--lime:hover,
.uichemy-halfsecond-1 .why__card--flat:hover {
transform: translateY(-4px);
}
.uichemy-halfsecond-1 .why__card--flat:hover {
border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.uichemy-halfsecond-1 .why__card--photo .why__body {
transition: transform .7s var(--ease);
}
.uichemy-halfsecond-1 .why__card--photo:hover .why__body {
transform: translateY(-3px);
}
.uichemy-halfsecond-1 .why__stat {
font-variant-numeric: tabular-nums;
}
.uichemy-halfsecond-1 .ledger__list li[data-reveal] {
--ry: 11px;
}
.uichemy-halfsecond-1 .foot__col[data-reveal] {
--ry: 14px;
}
.uichemy-halfsecond-1 .why__card[data-reveal] {
--ry: 0px;
}
.uichemy-halfsecond-1 .tw-c {
opacity: 0; transition: opacity .13s linear;
}
.uichemy-halfsecond-1 .tw-c.is-on {
opacity: 1;
}
.uichemy-halfsecond-1 .tw-caret {
position: relative; display: inline-block; width: 0;
}
.uichemy-halfsecond-1 .tw-caret::after {
content: ""; position: absolute;
left: .04em; top: .1em; bottom: .16em; width: .06em;
background: var(--accent);
animation: hs-twblink .85s steps(1) infinite;
}
@keyframes hs-twblink { 0%, 50% { opacity: 1 } 50.01%, 100% { opacity: 0 } }
@media (prefers-reduced-motion: reduce) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .ledger__list li:target {
animation: none;
box-shadow: inset 4px 0 0 var(--accent);
}
.uichemy-halfsecond-1 .faq__a,
.uichemy-halfsecond-1 .faq__a p,
.uichemy-halfsecond-1 .faq__mark,
.uichemy-halfsecond-1 .faq__mark::after {
transition: none;
}
.uichemy-halfsecond-1 .why__card--photo img {
transition: none;
}
.uichemy-halfsecond-1 .why__card--lime,
.uichemy-halfsecond-1 .why__card--flat,
.uichemy-halfsecond-1 .why__card--lime:hover,
.uichemy-halfsecond-1 .why__card--flat:hover {
transition: none; transform: none;
}
.uichemy-halfsecond-1 .why__card--photo:hover img {
scale: 1;
}
.uichemy-halfsecond-1 .why__card--photo:hover .why__body {
transform: none;
}
}
.uichemy-halfsecond-1 .figures {
position: relative; z-index: 2;
height: 360svh;
background: var(--bg);
}
.uichemy-halfsecond-1 .figures__pin {
position: sticky; top: 0;
height: 100vh;
height: 100svh;
overflow: hidden;
padding: clamp(46px, 7vh, 96px) var(--gutter) clamp(52px, 8vh, 104px);
display: flex; flex-direction: column;
}
.uichemy-halfsecond-1 .figures__eyebrow {
margin: 0;
}
.uichemy-halfsecond-1 .figures__num,
.uichemy-halfsecond-1 .figures__lab {
overflow: hidden;
}
.uichemy-halfsecond-1 .figures__num {
--fig-line: clamp(62px, 12.5vw, 210px); margin: auto 0;
}
.uichemy-halfsecond-1 .figures__lab {
--fig-line: clamp(26px, 3.6vw, 62px); text-align: right;
}
.uichemy-halfsecond-1 .figures__num,
.uichemy-halfsecond-1 .figures__lab {
height: var(--fig-line);
}
.uichemy-halfsecond-1 .figures__stack {
list-style: none; margin: 0; padding: 0;
transform: translateY(calc(var(--fig-i, 0) * var(--fig-line) * -1));
will-change: transform;
}
.uichemy-halfsecond-1 .figures__stack li {
height: var(--fig-line);
display: flex; align-items: center;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
letter-spacing: -.045em; line-height: 1;
text-transform: uppercase;
white-space: nowrap;
}
.uichemy-halfsecond-1 .figures__num .figures__stack li {
justify-content: center;
font-size: calc(var(--fig-line) * .86);
font-variant-numeric: tabular-nums;
color: var(--ink);
}
.uichemy-halfsecond-1 .figures__num small {
font-size: .5em; color: var(--accent);
}
.uichemy-halfsecond-1 .figures__lab .figures__stack li {
justify-content: flex-end;
font-size: calc(var(--fig-line) * .58);
letter-spacing: -.02em;
color: var(--ink-mute);
}
.uichemy-halfsecond-1 .figures__bar {
position: absolute;
top: clamp(46px, 7vh, 96px);
bottom: clamp(52px, 8vh, 104px);
right: calc(var(--gutter) * .42);
width: 2px;
background: var(--hair);
}
.uichemy-halfsecond-1 .figures__bar i {
display: block; width: 100%; height: 100%;
background: var(--accent);
transform: scaleY(0); transform-origin: 50% 0;
}
@media (max-width: 860px) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .figures {
height: 320svh;
}
.uichemy-halfsecond-1 .figures__num {
--fig-line: clamp(52px, 19vw, 110px);
}
.uichemy-halfsecond-1 .figures__lab {
--fig-line: clamp(22px, 6.4vw, 40px);
}
}
@media (prefers-reduced-motion: reduce) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .figures,
.uichemy-halfsecond-1 .figures__pin {
height: auto; position: static; overflow: visible;
}
.uichemy-halfsecond-1 .figures__num,
.uichemy-halfsecond-1 .figures__lab {
height: auto; overflow: visible;
}
.uichemy-halfsecond-1 .figures__stack {
transform: none;
}
.uichemy-halfsecond-1 .figures__bar {
display: none;
}
}
.uichemy-halfsecond-1 .ledger {
position: relative; z-index: 2;
background: var(--bg-deep);
padding: clamp(108px, 17vh, 216px) var(--gutter) clamp(104px, 16vh, 205px);
}
.uichemy-halfsecond-1 .ledger__title {
margin: 0 0 clamp(34px, 5.5vh, 66px);
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(34px, 6vw, 96px);
line-height: .92; letter-spacing: -.035em;
text-transform: uppercase;
color: var(--ink);
}
.uichemy-halfsecond-1 .ledger__list {
list-style: none; margin: 0; padding: 0; counter-reset: row;
}
.uichemy-halfsecond-1 .ledger__list li {
position: relative;
counter-increment: row;
display: grid;
grid-template-columns: 4ch 1fr auto 9ch;
gap: clamp(12px, 2vw, 40px);
align-items: baseline;
padding: clamp(13px, 2vh, 24px) 0;
border-top: 1px solid var(--hair);
font-size: clamp(13px, 1.05vw, 16px);
transition: color .35s var(--ease);
}
.uichemy-halfsecond-1 .ledger__list li:hover {
color: var(--accent);
}
.uichemy-halfsecond-1 .ledger__list li:target {
animation: hs-arrive 2.4s var(--ease) both;
}
@keyframes hs-arrive {
0%, 16% { background: color-mix(in srgb, var(--ink) 9%, transparent);
box-shadow: inset 4px 0 0 var(--accent); }
100% { background: transparent;
box-shadow: inset 0 0 0 transparent; }
}
.uichemy-halfsecond-1 .ledger__list li::before {
content: counter(row, decimal-leading-zero);
font-variant-numeric: tabular-nums;
color: var(--ink-mute); font-size: .86em; letter-spacing: .12em;
}
.uichemy-halfsecond-1 .ledger__list b {
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(19px, 2.4vw, 40px); letter-spacing: -.028em;
text-transform: uppercase; line-height: 1;
}
.uichemy-halfsecond-1 .ledger__list span {
color: var(--ink-mute); letter-spacing: .06em; text-transform: uppercase;
}
.uichemy-halfsecond-1 .ledger__list i {
font-style: normal; text-align: right;
font-variant-numeric: tabular-nums;
letter-spacing: .06em;
color: var(--accent);
}
.uichemy-halfsecond-1 .ledger__list li:hover {
z-index: 5;
}
.uichemy-halfsecond-1 .ledger__peek {
position: absolute; left: 46%; top: 50%; z-index: 5;
width: clamp(86px, 8.6vw, 128px);
height: auto;
aspect-ratio: 4 / 5;
object-fit: cover;
opacity: 0;
pointer-events: none;
transform: translate(-50%, -50%) scale(.82) rotate(-5deg);
transition: opacity .3s var(--ease), transform .48s var(--ease);
}
.uichemy-halfsecond-1 .ledger__list li:hover .ledger__peek {
opacity: 1;
transform: translate(-50%, -50%) scale(1) rotate(-2.5deg);
}
@media (hover: none), (max-width: 860px) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .ledger__peek {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .ledger__peek {
transition: none;
}
.uichemy-halfsecond-1 .ledger__list li:hover .ledger__peek {
transform: translate(-50%, -50%) rotate(-2.5deg);
}
}
.uichemy-halfsecond-1 .join {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) auto;
gap: clamp(16px, 3vw, 48px);
align-items: start;
margin-top: clamp(34px, 5vh, 64px);
padding-top: clamp(26px, 4vh, 44px);
border-top: 2px solid var(--accent);
}
.uichemy-halfsecond-1 .join__lead {
margin: 0;
font: 500 clamp(11px, .95vw, 13px)/1.4 var(--font-ui);
letter-spacing: .18em; text-transform: uppercase;
color: var(--ink-mute);
}
.uichemy-halfsecond-1 .join__lead b {
display: block; margin-top: .3em;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(30px, 3.6vw, 58px);
letter-spacing: -.03em; line-height: 1;
font-variant-numeric: tabular-nums;
color: var(--accent);
}
.uichemy-halfsecond-1 .join__body {
margin: 0; max-width: 46ch;
font: 400 clamp(13px, 1.05vw, 16px)/1.5 var(--font-ui);
color: var(--ink-mute);
}
.uichemy-halfsecond-1 .join .cta__go {
align-self: center;
}
.uichemy-halfsecond-1 .ledger {
--accent-ink: #748A1A;
--bg: #EDEBE4;
--ink: #131409;
--ink-mute: #5D6055;
--hair: rgba(19, 20, 9, .17);
background: var(--bg);
color: var(--ink);
}
.uichemy-halfsecond-1 .ledger__list li:hover {
color: var(--ink);
background: color-mix(in srgb, var(--ink) 5%, transparent);
}
.uichemy-halfsecond-1 .ledger__list li:hover::before {
color: var(--ink);
}
.uichemy-halfsecond-1 .ledger__list i {
justify-self: end;
padding: .2em .5em .16em;
background: var(--accent);
color: var(--ink);
}
.uichemy-halfsecond-1 .join {
border-top: 2px solid var(--ink);
}
.uichemy-halfsecond-1 .join__lead b {
color: var(--ink);
}
.uichemy-halfsecond-1 .join .cta__go {
background: var(--ink); color: var(--bg);
}
.uichemy-halfsecond-1 .join .cta__go:hover {
filter: brightness(1.6);
}
.uichemy-halfsecond-1 .voices {
position: relative; z-index: 2;
overflow-x: clip;
background: var(--bg);
padding: clamp(74px, 12vh, 168px) var(--gutter) clamp(78px, 13vh, 180px);
--vx: clamp(76px, 9.5vw, 158px);
--vq: clamp(26px, 3.4vw, 56px);
}
.uichemy-halfsecond-1 .voices__head {
margin: 0 0 clamp(44px, 7vh, 92px);
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(32px, 5.4vw, 96px);
line-height: .93; letter-spacing: -.04em;
text-transform: uppercase;
color: var(--ink);
}
.uichemy-halfsecond-1 .voices__list {
list-style: none; margin: 0; padding: 0;
display: grid; gap: clamp(30px, 5vh, 68px);
}
.uichemy-halfsecond-1 .voices__item {
display: grid;
grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
gap: clamp(20px, 3.4vw, 60px);
align-items: center;
padding-top: clamp(30px, 5vh, 68px);
border-top: 1px solid var(--hair);
}
.uichemy-halfsecond-1 .voices__item:nth-child(even) {
grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
}
.uichemy-halfsecond-1 .voices__item:nth-child(even) .voices__shot {
grid-column: 2; grid-row: 1;
}
.uichemy-halfsecond-1 .voices__item[data-reveal] {
--ry: 0px; --rx: calc(0px - var(--vx)); --sx: calc(0px - var(--vq));
}
.uichemy-halfsecond-1 .voices__item:nth-child(even)[data-reveal] {
--rx: var(--vx); --sx: var(--vq);
}
.uichemy-halfsecond-1 .voices__shot {
position: relative; margin: 0; overflow: hidden;
aspect-ratio: 5 / 4;
background: #17181B;
}
.uichemy-halfsecond-1 .voices__shot img {
width: 100%; height: 100%;
object-fit: cover;
}
.uichemy-halfsecond-1 .voices__bib {
position: absolute; left: 5%; top: 4%;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(15px, 2.4cqw, 30px);
line-height: 1; letter-spacing: -.02em;
font-variant-numeric: tabular-nums;
color: rgb(255, 255, 255); opacity: .9;
text-shadow: 0 1px 12px rgba(0,0,0,.5);
}
.uichemy-halfsecond-1 .voices__shot {
container-type: inline-size;
}
.uichemy-halfsecond-1 .voices__say {
margin: 0;
}
.uichemy-halfsecond-1 .voices__say p {
margin: 0 0 clamp(16px, 2.4vh, 30px);
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(19px, 2.5vw, 42px);
line-height: 1.06; letter-spacing: -.028em;
text-transform: uppercase;
color: var(--ink);
text-wrap: pretty;
}
.uichemy-halfsecond-1 .voices__who {
display: flex; align-items: center; flex-wrap: wrap; gap: .7em;
font: 600 clamp(10.5px, .88vw, 13px)/1 var(--font-ui);
letter-spacing: .18em; text-transform: uppercase;
color: var(--ink);
}
.uichemy-halfsecond-1 .voices__who span,
.uichemy-halfsecond-1 .voices__who a {
padding: .34em .6em .28em;
background: var(--accent);
letter-spacing: .16em;
color: #131409;
text-decoration: none;
}
.uichemy-halfsecond-1 .voices__who a {
transition: filter .2s var(--ease);
}
.uichemy-halfsecond-1 .voices__who a:hover {
filter: brightness(1.18);
}
@media (max-width: 860px) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .voices {
--vx: 0px; --vq: 0px;
}
.uichemy-halfsecond-1 .voices__item,
.uichemy-halfsecond-1 .voices__item:nth-child(even) {
grid-template-columns: 1fr; gap: 20px;
}
.uichemy-halfsecond-1 .voices__item:nth-child(even) .voices__shot {
grid-column: 1; grid-row: 1;
}
.uichemy-halfsecond-1 .voices__item[data-reveal],
.uichemy-halfsecond-1 .voices__item:nth-child(even)[data-reveal] {
--ry: 18px;
}
.uichemy-halfsecond-1 .voices__shot {
aspect-ratio: 3 / 2;
}
}
.uichemy-halfsecond-1 .faq {
--accent-ink: #748A1A;
--bg: #EDEBE4;
--ink: #131409;
--ink-mute: #5D6055;
--hair: rgba(19, 20, 9, .17);
position: relative; z-index: 2;
background: var(--bg);
color: var(--ink);
padding: clamp(104px, 16vh, 205px) var(--gutter) clamp(108px, 17vh, 216px);
display: grid;
grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
gap: clamp(30px, 5vw, 96px);
align-items: start;
}
.uichemy-halfsecond-1 .faq__head {
position: sticky; top: clamp(40px, 12vh, 120px);
}
.uichemy-halfsecond-1 .faq__eyebrow {
margin: 0 0 clamp(14px, 1.8vh, 24px);
font: 600 clamp(11px, .95vw, 14px)/1 var(--font-ui);
letter-spacing: .22em; text-transform: uppercase;
color: var(--ink);
}
.uichemy-halfsecond-1 .faq__title {
margin: 0 0 clamp(20px, 3vh, 36px);
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(32px, 4.6vw, 76px);
line-height: .93; letter-spacing: -.035em;
text-transform: uppercase;
}
.uichemy-halfsecond-1 .faq__note {
margin: 0; max-width: 30ch;
font-size: clamp(14px, 1.05vw, 17px); line-height: 1.5;
color: var(--ink-mute);
}
.uichemy-halfsecond-1 .faq__note a {
color: var(--ink); text-decoration: none;
box-shadow: inset 0 -1px var(--ink);
transition: background .2s var(--ease);
}
.uichemy-halfsecond-1 .faq__note a:hover {
background: var(--accent);
}
.uichemy-halfsecond-1 .faq__list {
list-style: none; margin: 0; padding: 0;
}
.uichemy-halfsecond-1 .faq__item {
border-top: 1px solid var(--hair);
}
.uichemy-halfsecond-1 .faq__item:last-child {
border-bottom: 1px solid var(--hair);
}
.uichemy-halfsecond-1 .faq__item[data-reveal] {
--ry: 14px;
}
.uichemy-halfsecond-1 .faq__row {
margin: 0; font: inherit; font-weight: inherit;
}
.uichemy-halfsecond-1 .faq__q {
width: 100%;
display: flex; align-items: center; justify-content: space-between;
gap: clamp(16px, 2.4vw, 34px);
padding: clamp(18px, 2.6vh, 32px) 0;
border: 0; background: none; cursor: pointer; text-align: left;
color: inherit;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(17px, 1.75vw, 28px);
line-height: 1.08; letter-spacing: -.024em;
text-transform: uppercase;
}
.uichemy-halfsecond-1 .faq__q:focus-visible {
outline: 2px solid var(--ink); outline-offset: -3px;
}
.uichemy-halfsecond-1 .faq__mark {
position: relative; flex: none;
width: clamp(15px, 1.4vw, 21px); aspect-ratio: 1;
}
.uichemy-halfsecond-1 .faq__mark::before,
.uichemy-halfsecond-1 .faq__mark::after {
content: ""; position: absolute; inset: 0; margin: auto;
background: var(--ink-mute);
transition: background .3s var(--ease);
}
.uichemy-halfsecond-1 .faq__mark::before {
width: 100%; height: 1.8px;
}
.uichemy-halfsecond-1 .faq__mark::after {
width: 1.8px; height: 100%;
transition: transform .35s var(--ease), background .3s var(--ease);
}
.uichemy-halfsecond-1 .faq__q:hover .faq__mark::before,
.uichemy-halfsecond-1 .faq__q:hover .faq__mark::after {
background: var(--ink);
}
.uichemy-halfsecond-1 .faq__item.is-open .faq__mark::before {
background: var(--accent-ink); height: 2.2px;
}
.uichemy-halfsecond-1 .faq__item.is-open .faq__mark::after {
transform: scaleY(0);
}
.uichemy-halfsecond-1 .faq__a {
display: grid; grid-template-rows: 0fr;
transition: grid-template-rows .5s var(--in-ease);
}
.uichemy-halfsecond-1 .faq__a > div {
overflow: hidden;
}
.uichemy-halfsecond-1 .faq__item.is-open .faq__a {
grid-template-rows: 1fr;
}
.uichemy-halfsecond-1 .faq__a p {
margin: 0;
padding: 0 clamp(46px, 5vw, 76px) clamp(22px, 3.2vh, 36px) 0;
max-width: 60ch;
font-size: clamp(15px, 1.2vw, 18.5px); line-height: 1.55;
color: var(--ink-mute);
opacity: 0; transform: translateY(6px);
transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.uichemy-halfsecond-1 .faq__item.is-open .faq__a p {
opacity: 1; transform: none;
transition-delay: .12s;
}
@media (max-width: 900px) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .faq {
grid-template-columns: 1fr; gap: clamp(26px, 4.5vh, 48px);
}
.uichemy-halfsecond-1 .faq__head {
position: static;
}
.uichemy-halfsecond-1 .faq__a p {
padding-right: 0;
}
}
.uichemy-halfsecond-1 .foot {
position: relative; isolation: isolate;
padding: clamp(12px, 1.5vw, 22px);
background: var(--bg);
overflow: hidden;
}
.uichemy-halfsecond-1 .foot__bg {
position: absolute; inset: 0; z-index: -2;
width: 100%; height: 100%;
object-fit: cover;
opacity: .7;
filter: saturate(1.35) contrast(1.05);
}
.uichemy-halfsecond-1 .foot::after {
content: ""; position: absolute; inset: 0; z-index: -1;
background: linear-gradient(rgba(8,9,11,.3), rgba(8,9,11,.72));
}
.uichemy-halfsecond-1 .foot__panel {
display: flex; flex-direction: column;
min-height: min(86svh, 820px);
border-radius: clamp(14px, 1.8vw, 26px);
background: rgba(8,9,11,.52);
padding: clamp(36px, 5.4vw, 86px) clamp(22px, 3.4vw, 58px) clamp(22px, 2.6vw, 38px);
}
.uichemy-halfsecond-1 .foot__top {
flex: 1;
align-content: start;
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
gap: clamp(30px, 5vh, 60px) clamp(28px, 5vw, 96px);
align-items: start;
}
.uichemy-halfsecond-1 .foot__ask {
grid-column: 1;
}
.uichemy-halfsecond-1 .foot__col--a {
grid-column: 2;
}
.uichemy-halfsecond-1 .foot__col--b {
grid-column: 3;
}
.uichemy-halfsecond-1 .foot__head {
margin: 0 0 clamp(20px, 2.4vw, 34px);
max-width: 18ch;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(26px, 3.3vw, 58px);
line-height: 1.0; letter-spacing: -.032em;
text-transform: uppercase;
color: var(--ink);
}
.uichemy-halfsecond-1 .foot__col h3 {
margin: 0 0 clamp(12px, 1.4vw, 20px);
font: 600 clamp(11px, .95vw, 14px)/1 var(--font-ui);
letter-spacing: .2em; text-transform: uppercase;
color: var(--ink);
}
.uichemy-halfsecond-1 .foot__col ul {
list-style: none; margin: 0; padding: 0; display: grid; gap: .7em;
}
.uichemy-halfsecond-1 .foot__col li {
font: 400 clamp(12.5px, 1.05vw, 15px)/1.35 var(--font-ui);
color: var(--ink-mute);
}
.uichemy-halfsecond-1 .foot__col a {
color: inherit; text-decoration: none; transition: color .3s var(--ease);
}
.uichemy-halfsecond-1 .foot__col a:hover {
color: var(--accent);
}
.uichemy-halfsecond-1 .foot__base {
display: flex; align-items: flex-end; justify-content: space-between;
gap: clamp(16px, 3vw, 40px);
margin-top: clamp(30px, 4.5vh, 56px);
padding-top: clamp(16px, 2.2vw, 28px);
border-top: 1px solid var(--hair);
}
.uichemy-halfsecond-1 .foot__mark {
margin: 0;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(40px, 8.6vw, 148px);
line-height: .8; letter-spacing: -.045em;
text-transform: uppercase;
color: color-mix(in srgb, var(--ink) 20%, transparent);
-webkit-user-select: none; user-select: none;
}
.uichemy-halfsecond-1 .foot__legal {
margin: 0 0 .8em; display: flex; gap: clamp(14px, 2vw, 30px);
}
.uichemy-halfsecond-1 .foot__legal a {
font: 500 clamp(10.5px, .9vw, 13px)/1 var(--font-ui);
letter-spacing: .14em; text-transform: uppercase;
color: var(--ink-mute); text-decoration: none;
transition: color .3s var(--ease);
}
.uichemy-halfsecond-1 .foot__legal a:hover {
color: var(--accent);
}
.uichemy-halfsecond-1 {
--in-ease: cubic-bezier(.16,.84,.34,1);
}
.uichemy-halfsecond-1 .load {
display: none;
}
.uichemy-halfsecond-1 .load__mark {
position: absolute; left: var(--gutter); top: clamp(15px, 2.2vw, 24px);
margin: 0;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(15px, 1.35vw, 20px);
letter-spacing: -.01em; text-transform: uppercase;
color: var(--ink);
}
.uichemy-halfsecond-1 .load__num {
position: absolute; left: var(--gutter); bottom: clamp(34px, 6vh, 74px);
margin: 0;
display: flex; align-items: baseline; gap: .12em;
font-family: var(--font-display);
font-weight: 800; font-stretch: 84%; font-style: italic;
font-size: clamp(72px, 15vw, 230px);
line-height: .78; letter-spacing: -.05em;
font-variant-numeric: tabular-nums;
color: var(--ink);
}
.uichemy-halfsecond-1 .load__num small {
font-size: .2em; letter-spacing: .04em;
color: var(--accent);
}
.uichemy-halfsecond-1 .load__bar {
position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
background: rgba(244,245,243,.12);
}
.uichemy-halfsecond-1 .load__bar i {
display: block; height: 100%;
background: var(--accent);
transform: scaleX(0); transform-origin: 0 50%;
transition: transform .35s var(--in-ease);
}
.uichemy-halfsecond-1 .type__line span {
display: block;
}
.uichemy-halfsecond-1 .scrollcue {
position: absolute; left: 50%; top: 47.5%;
transform: translate(-50%, -50%);
width: clamp(92px, 13.5vh, 152px); aspect-ratio: 1;
opacity: var(--cue-o, 1);
pointer-events: none;
}
.uichemy-halfsecond-1 .scrollcue__ring {
display: block; width: 100%; height: 100%;
animation: hs-cuespin 18s linear infinite;
}
@keyframes hs-cuespin { to { transform: rotate(360deg); } }
.uichemy-halfsecond-1 .scrollcue__edge {
fill: none;
stroke: var(--hair);
stroke-width: .7;
}
.uichemy-halfsecond-1 .scrollcue__text {
fill: var(--ink-mute);
font-family: var(--font-ui);
font-size: 8.4px; font-weight: 600;
letter-spacing: .04em; text-transform: uppercase;
}
.uichemy-halfsecond-1 .scrollcue__arrow {
position: absolute; left: 50%; top: 50%;
width: 20%; height: 20%;
border-right: 2px solid var(--accent);
border-bottom: 2px solid var(--accent);
animation: hs-cuedrop 2.1s var(--in-ease) infinite;
}
@keyframes hs-cuedrop {
0%, 100% { transform: translate(-50%, -66%) rotate(45deg); opacity: .45; }
50% { transform: translate(-50%, -34%) rotate(45deg); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .plate figcaption,
.uichemy-halfsecond-1 .plate::after {
transition: none;
}
.uichemy-halfsecond-1 .plate figcaption {
transform: none;
}
.uichemy-halfsecond-1 .scrollcue__ring {
animation: none;
}
.uichemy-halfsecond-1 .scrollcue__arrow {
animation: none; opacity: 1;
transform: translate(-50%, -50%) rotate(45deg);
}
}
@media (hover: none), (pointer: coarse), (max-width: 860px) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .nav__link,
.uichemy-halfsecond-1 .foot__col a,
.uichemy-halfsecond-1 .foot__legal a,
.uichemy-halfsecond-1 .faq__note a,
.uichemy-halfsecond-1 .voices__who a {
display: inline-block;
padding-block: 11px;
}
.uichemy-halfsecond-1 .nav__mark {
padding-block: 9px;
}
.uichemy-halfsecond-1 .foot__col--b li {
padding-block: 9px;
}
.uichemy-halfsecond-1 .nav__link {
padding-block: 9px;
}
.uichemy-halfsecond-1 .cta__go {
padding: 1.15em 1.4em;
}
.uichemy-halfsecond-1 .faq__q {
padding-block: clamp(20px, 2.8vh, 32px);
}
.uichemy-halfsecond-1 .ledger__peek {
display: none;
}
.uichemy-halfsecond-1 .plate figcaption {
opacity: 1; transform: none;
}
.uichemy-halfsecond-1 .plate::after {
opacity: 1;
}
}
@media (max-width: 860px) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 {
--grid-cols: 2;
--grid-h: min(60svh, 540px);
--row-h: calc((var(--grid-h) - var(--row-gap) * 2) / 3);
--grid-w: min(86vw, calc(var(--row-h) * 1.05 * 2 + var(--grid-gap)));
}
.uichemy-halfsecond-1 .scene {
height: 440svh;
}
.uichemy-halfsecond-1 .nav {
flex-wrap: wrap; row-gap: 6px;
}
.uichemy-halfsecond-1 .nav__hours {
order: 3;
}
.uichemy-halfsecond-1 .type__line {
font-size: clamp(44px, min(14vw, 17vh), 96px);
}
.uichemy-halfsecond-1 .type__line--a {
top: 28%;
}
.uichemy-halfsecond-1 .type__line--b {
top: 54%;
}
.uichemy-halfsecond-1 .work {
top: 52%;
}
.uichemy-halfsecond-1 .work__grid > :nth-child(n+7) {
display: none;
}
.uichemy-halfsecond-1 .work__grid::before {
top: calc(var(--row-h) * 1.5 + var(--row-gap) * .75);
}
.uichemy-halfsecond-1 .note {
max-width: 24ch; bottom: 18px; font-size: 12px;
}
.uichemy-halfsecond-1 .cta {
bottom: 16px; gap: .5em;
}
.uichemy-halfsecond-1 .cta__go {
padding: 1em 1.25em; font-size: 11.5px; letter-spacing: .12em; gap: .8em;
}
.uichemy-halfsecond-1 .cta__note {
font-size: 11px; letter-spacing: .1em;
}
.uichemy-halfsecond-1 .join {
grid-template-columns: 1fr; gap: 18px;
}
.uichemy-halfsecond-1 .join .cta__go {
justify-self: start;
}
.uichemy-halfsecond-1 .ledger__list li {
grid-template-columns: 3.5ch 1fr auto;
}
.uichemy-halfsecond-1 .ledger__list i {
grid-column: 3;
}
.uichemy-halfsecond-1 .ledger__list span {
grid-column: 2; grid-row: 2;
}
.uichemy-halfsecond-1 .why__head {
max-width: 100%;
}
.uichemy-halfsecond-1 .why__grid {
grid-template-columns: 1fr;
}
.uichemy-halfsecond-1 .why__card--wide,
.uichemy-halfsecond-1 .why__card--narrow {
grid-column: 1;
}
.uichemy-halfsecond-1 .why__card {
min-height: 220px;
}
.uichemy-halfsecond-1 .scrollcue {
top: 41%; width: clamp(56px, 10vh, 84px);
}
.uichemy-halfsecond-1 .scrollcue__text {
font-size: 9.4px;
}
.uichemy-halfsecond-1 .load__num {
font-size: clamp(64px, 26vw, 120px);
}
.uichemy-halfsecond-1 .foot__top {
grid-template-columns: 1fr 1fr; gap: 30px clamp(18px, 5vw, 40px);
}
.uichemy-halfsecond-1 .foot__ask {
grid-column: 1 / -1;
}
.uichemy-halfsecond-1 .foot__col--a {
grid-column: 1;
}
.uichemy-halfsecond-1 .foot__col--b {
grid-column: 2;
}
.uichemy-halfsecond-1 .foot__base {
flex-direction: column; align-items: flex-start; gap: 22px;
}
.uichemy-halfsecond-1 .foot__legal {
margin: 0;
}
}
@media (min-width: 620px) and (max-width: 860px) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 {
--grid-cols: 3;
--grid-h: min(56svh, 620px);
--row-h: calc((var(--grid-h) - var(--row-gap)) / 2);
--grid-w: min(92vw, calc(var(--row-h) * 1.05 * 3 + var(--grid-gap) * 2));
}
.uichemy-halfsecond-1 .work__grid::before {
top: calc(var(--row-h) + var(--row-gap) / 2);
}
.uichemy-halfsecond-1 .foot__top {
grid-template-columns: 1fr 1fr 1fr;
}
.uichemy-halfsecond-1 .foot__ask {
grid-column: 1 / -1;
}
.uichemy-halfsecond-1 .foot__col--a {
grid-column: 2;
}
.uichemy-halfsecond-1 .foot__col--b {
grid-column: 3;
}
}
@media (prefers-reduced-motion: reduce) {
.uichemy-halfsecond-1 { --hs-guard: 0; }
.uichemy-halfsecond-1 .scene {
height: auto;
}
.uichemy-halfsecond-1 .scene__pin {
position: static; height: auto; overflow: visible;
padding: clamp(110px, 17vh, 190px) var(--gutter) clamp(60px, 9vh, 110px);
}
.uichemy-halfsecond-1 .rings {
display: none;
}
.uichemy-halfsecond-1 .type {
position: static;
}
.uichemy-halfsecond-1 .type__line {
position: static; transform: none; width: auto; white-space: normal;
}
.uichemy-halfsecond-1 .work {
position: static; transform: none; width: 100%;
margin: clamp(36px, 6vh, 70px) 0;
}
.uichemy-halfsecond-1 .work__grid::before {
display: none;
}
.uichemy-halfsecond-1 .note {
position: static; margin: 0 0 18px;
}
.uichemy-halfsecond-1 .cta {
position: static; align-items: flex-start; margin: 0 0 18px;
}
.uichemy-halfsecond-1 .cta__go,
.uichemy-halfsecond-1 .cta__go i,
.uichemy-halfsecond-1 .cta__go:hover,
.uichemy-halfsecond-1 .cta__go:hover i {
transition: none; transform: none;
}
.uichemy-halfsecond-1 .ledger__list li {
transition: none;
}
}
```