Back to the library
Pilotis panel lift, slow beat
Variant: Slow beat
The same panel entrance stretched to a heavier single beat — the slab takes roughly twice as long to settle and the dwell before the loop repeats is longer. Use it on large surfaces such as a full-width section or a drawer, where a fast arrival reads as a flicker. Geometry, travel and the ease-out curve are identical to the single-beat sibling; only the clock changed.
entrancecardrevealarchitecturalloop
The source
A complete HTML document. Paste it into an empty .html file and it plays — no stylesheet, script, font, or image to fetch.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Pilotis panel lift — slow beat</title>
<style>
:root {
/* Neutrals — fixed, never remixed. */
--ivory: #faf9f5;
--paper: #ffffff;
--oat: #e3dacc;
--slate: #141413;
/* Knobs — the remix API. A knob exists iff declared here and used below. */
--hue: 38.8;
--lift: 0;
--chroma: 1;
--round: 1;
--speed: 1;
/* Accents — derived from the knobs with the family's offsets baked in,
so one hue roll moves them together. Defaults reproduce the original hex
(#d97757 / #b85c3e / #788c5d / #b04a3f). */
--clay: oklch(calc(0.6724 + var(--lift)) calc(0.1308 * var(--chroma)) var(--hue));
--clay-d: oklch(calc(0.5797 + var(--lift)) calc(0.127 * var(--chroma)) var(--hue));
--olive: oklch(calc(0.6118 + var(--lift)) calc(0.0713 * var(--chroma)) calc(var(--hue) + 88.3));
--rust: oklch(calc(0.5408 + var(--lift)) calc(0.1357 * var(--chroma)) calc(var(--hue) - 10.3));
/* Clock — every duration in the document runs off --t. */
--cycle: 4.4s;
--t: calc(var(--cycle) / var(--speed));
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--_enter: calc(0.46s / var(--speed));
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body {
min-height: 100vh;
display: grid;
place-items: center;
background: var(--ivory);
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
color: var(--slate);
padding: clamp(10px, 4vw, 24px);
}
.stage {
width: min(400px, 90vw);
display: grid;
gap: clamp(8px, 2.4vw, 12px);
}
.panel {
position: relative;
background: var(--paper);
border: 1px solid var(--oat);
border-radius: calc(4px * var(--round));
padding: clamp(12px, 3.6vw, 18px);
display: grid;
gap: clamp(9px, 2.6vw, 13px);
}
.roof {
height: clamp(8px, 2.4vw, 10px);
border-radius: calc(2px * var(--round));
background: var(--olive);
transform-origin: 0% 100%;
animation: roof-set var(--t) var(--ease-out) infinite;
}
.slab {
height: clamp(46px, 15vw, 62px);
border-radius: calc(3px * var(--round));
background:
linear-gradient(90deg, var(--clay) 0 38%, var(--clay-d) 38% 100%);
animation: slab-lift var(--t) var(--ease-out) infinite;
}
.ribbon {
position: absolute;
left: clamp(12px, 3.6vw, 18px);
right: clamp(12px, 3.6vw, 18px);
top: calc(clamp(12px, 3.6vw, 18px) + clamp(8px, 2.4vw, 10px) + clamp(9px, 2.6vw, 13px) + clamp(14px, 4.4vw, 19px));
height: clamp(14px, 4vw, 18px);
background: var(--ivory);
border-radius: calc(2px * var(--round));
transform-origin: 0% 50%;
animation: ribbon-open var(--t) var(--ease-out) infinite;
}
.piers {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: clamp(10px, 3.2vw, 16px);
height: clamp(20px, 6vw, 26px);
}
.pier {
background: var(--slate);
border-radius: calc(2px * var(--round));
transform-origin: 50% 100%;
animation: pier-raise var(--t) var(--ease-out) infinite;
}
.caption {
font-size: clamp(11px, 3vw, 12px);
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--slate);
opacity: 0.5;
text-align: center;
}
/* 10.5% of a 4.4s loop ≈ 460ms — twice the single-beat entrance,
with the extra loop length spent dwelling rather than travelling. */
@keyframes slab-lift {
0% { opacity: 0; transform: translate3d(0, 5%, 0) scale(0.97); }
10.5% { opacity: 1; transform: none; }
100% { opacity: 1; transform: none; }
}
@keyframes roof-set {
0% { opacity: 0; transform: translate3d(0, 6px, 0) scaleX(0.94); }
10.5% { opacity: 1; transform: none; }
100% { opacity: 1; transform: none; }
}
@keyframes ribbon-open {
0% { opacity: 0; transform: scaleX(0.04); }
10.5% { opacity: 1; transform: scaleX(1); }
100% { opacity: 1; transform: scaleX(1); }
}
@keyframes pier-raise {
0% { opacity: 0; transform: scaleY(0.35); }
10.5% { opacity: 1; transform: scaleY(1); }
100% { opacity: 1; transform: scaleY(1); }
}
@media (max-width: 380px) {
.panel { padding: 12px; }
.caption { letter-spacing: 0.1em; }
}
@media (prefers-reduced-motion: reduce) {
.slab, .roof, .ribbon, .pier {
animation: soft-breathe calc(var(--cycle) * 2.4 / var(--speed)) ease-in-out infinite;
transform: none;
}
@keyframes soft-breathe {
0%, 100% { opacity: 1; }
50% { opacity: 0.62; }
}
}
</style>
</head>
<body>
<div class="stage">
<div class="panel">
<div class="roof"></div>
<div class="slab"></div>
<div class="ribbon"></div>
<div class="piers">
<span class="pier"></span>
<span class="pier"></span>
<span class="pier"></span>
<span class="pier"></span>
<span class="pier"></span>
</div>
</div>
<p class="caption">Section ready</p>
</div>
</body>
</html>The variant family
Same idea, one axis moved. Compare them side by side before you commit.