Slide 01 / 07
Click the deck, then use the arrow keys like a real presentation.
Readout
- Family
- Signature
- Theme
- Dark
- Type
- Schibsted Grotesk
- Accent
- #ffa524
- Corners
- 8px
- Moves on
- Keys, wheel, swipe
- Studio
- Ready
04 of 30, Signature
Waterline.
One hairline runs across the frame like the surface of water, and every title is snapped in two where it crosses.
How it moves
A deep aqua room with different physics either side of a single line: above it things drift vertically, below it a viscous current carries them sideways and the deeper an object sits the more it lags. Use it when one idea per slide deserves a headline treated as an optical instrument rather than a caption.
This is the specification the Studio sends to the model, written for Waterline's own look. Change the look in the Studio and every value follows.
Palette
- Background#08151a
- Text#e6efee
- Muted#7d9498
- Accent#ffa524
- Surfacergba(230,239,238,.05)
- Hairlinergba(230,239,238,.13)
Type: Schibsted Grotesk. Corners: 8px.
Layout
- Ground. #08151a, and one idea carries the whole format. A fixed 1px full-bleed hairline, the waterline, sits at top: var(--surface), where --surface is 30vh. Its colour is built from the line token: --edge: color-mix(in oklab, rgba(230,239,238,.13) 85%, #e6efee), so the builder's own hairline value drives the format's datum. Above it is air (bare #08151a); below it a fixed .deep plane runs from 30vh to the bottom, filled linear-gradient(to bottom, var(--deepink), color-mix(in oklab, var(--deepink) 86%, #08151a)) with --deepink: color-mix(in oklab, #08151a 94.5%, #e6efee), a 5% step off the ground that attenuates again with depth. That tonal step plus the hairline is the entire stage; there is no other decoration.
- THE MEDIUM MUST STEP AWAY FROM THE TEXT, NOT TOWARD IT. On a pale ground (theme=light) mixing #08151a toward #e6efee darkens the plane and drags every --muted object standing on it under 4.5:1. So the format's JS reads the resolved body background once, before the first paint, and sets data-medium="pale" when its relative luminance is above .4; html[data-medium="pale"] then redefines --deepink as color-mix(in oklab, #08151a 93%, rgba(230,239,238,.05)) and flips the gradient's far end the same way. Measured: #7d9498 on the plane is 5.26:1 dark, 4.86:1 light.
- THE HEADLINE STRADDLES THE LINE AND IS PERMANENTLY BROKEN. .ttl is absolute at left 8vw / top var(--surface), font-size min(7.4vw, 11.4vh) (height-capped so it can never grow down into the content band), weight 600, letter-spacing -.035em, line-height 1.4, white-space nowrap, display grid, and transform: translateY(calc(var(--cut) * -1)) with --cut: 56%, which lands the fracture exactly ON the line. Three copies of the same words share grid-area 1/1: .f (#ffa524 at opacity .82, clip-path inset(calc(var(--cut) - 1px) 0 0 0)), .b (#e6efee, same bottom clip), .a (#e6efee, clip-path inset(0 0 calc(100% - var(--cut)) 0)). Every slide carries an inline --refract (authored -1.35 … 1.5 vw; the closing slide sets 0), and the amber copy sits a further --trap: max(.34vw, 3.2px) behind it, the px floor keeps the dispersion edge visible on a phone, where .34vw is 1.3px. One hard trap edge on one object, never on body copy.
- SIGNATURE MOTION, different physics either side of the boundary, all driven by the engine's --d, which each .slide first clamps to --step: clamp(-1, var(--d, 1), 1) so a seven-slide jump from the strip still travels one slide's worth. ABOVE the line one rigid object moves vertically: .a { transform: translateY(calc(var(--step) * -1.3vh)); transition: transform 950ms cubic-bezier(.22,1,.36,1) }. BELOW it everything is swept horizontally: .b { transform: translateX(calc((var(--refract) + var(--step) * 6) * 1vw)); transition: transform 950ms cubic-bezier(.65,0,.25,1) } and .f { transform: translateX(calc((var(--refract) + var(--step) * 7.4) * 1vw + var(--trap))); transition: transform 1150ms cubic-bezier(.65,0,.25,1) }. The amber copy has a different refractive index, same path, 200ms slower, 1.4vw further, so the lower half smears apart to a measured 37px (≈2.9vw) around t=490ms and re-converges to the .34vw trap by t=1400ms. One second, and you watch light bend.
- THE SAME LAW GOVERNS THE CONTENT, NOT JUST THE HEADLINE. .body is absolute at left 8vw, width 84vw, the identical 8vw/92vw grid as the strip, the mark and the HUD, so nothing is short of the margin at any size, top calc(var(--surface) + 12.4vh), bottom 7.5vh. It travels as bulk current: transform: translateX(calc(var(--step) * 7vw)) scale(calc(1 - min(var(--ad),3) * .012)); filter: blur(calc(min(var(--ad),4) * 3px)); opacity: calc(1 - var(--ad)); transition transform 950ms glide 120ms, filter 900ms ease 120ms, opacity 880ms ease 120ms. Inside it the medium is VISCOUS AND DEPTH-GRADED: the format's JS stamps --j on every .reveal in the engine's own order, and .body .reveal { transform: translateX(calc(var(--step) * (1.05vw + var(--j) * .62vw))); filter: blur(calc(.5px + var(--j) * .7px)); transition: opacity calc(420ms + var(--j) * 70ms) ease, transform calc(620ms + var(--j) * 135ms) glide, filter same } → .in { transform: none; filter: none }. The deeper an object sits in the band the further it is carried, the more it is defocused on the way and the later it settles, so the band shears into place instead of sliding in as one rigid plate. The offset is signed by --step, so nothing in the format ever moves against the current.
- The band is a composition, not a stack: .body is a flex column at justify-content flex-start, and .cards/.compare/.carousel/.timeline take flex: 1 1 auto so the principal object fills whatever the lede leaves. .card copy settles to the bottom of its panel (justify-content: flex-end), .compare panels centre theirs, and .rings is forced to grid-template-columns: repeat(3, 1fr) so the rings stand on the same three columns as the stat tiles under them. The outgoing state leaves in about a third of the time but still disperses: .slide:not([data-state="active"]) .ttl 380ms, its .a/.b 420ms, its .f 540ms, its .body 380ms with the delay removed, its reveals 300ms.
- ORIENTATION, THE SURFACE. The hairline is nav datum, progress geometry and physical boundary at once. Hung under it at top calc(var(--surface) + 7.4vh), spanning 8vw to 92vw as a space-between flex row, is a .strip of all seven titles as real <button class="tab" data-goto> at clamp(.56rem,.9vw,.78rem) / 500 / .08em / uppercase. Three states, ALL AT FULL OPACITY because they are real controls at body size: future #7d9498 (5.26:1 on the plane), done color-mix(in oklab, #7d9498 45%, #e6efee), active #e6efee plus translateY(-3px) and a 1px #ffa524 rule scaling in from the left over .6s. The second, continuous geometry is the line itself: .surface .wet is the same hairline BRIGHTENED, color-mix(in oklab, #e6efee 58%, transparent), transform: scaleX(var(--progress)) from the left over 1050ms glide. It is deliberately not amber: the accent on that line belongs to the caustic alone, so the wetted length and the light can never be read as the same mark.
- THE CAUSTIC, AND WHY IT IS AT z-index 0. A 10vw x 2px #ffa524 sliver with blur(.7px) rides the line at transform: translateX(calc(var(--cx) - 5vw)), transition 1050ms glide, where --cx is set on <html> by JS as the centre of the active tab (re-measured on resize and on document.fonts.ready, guarded because deck.i is -1 before the engine's first rAF). Its inner <i> replays @keyframes smear { 0% scaleX(1); 42% scaleX(2.6); 100% scaleX(1) } for 1050ms on every change. It must sit BEHIND the headline or it paints a bright bar straight across the glyphs, and because .stage is position:fixed it opens its own stacking context, which traps the title's z-index 3 inside it, so raising the title cannot work. The caustic therefore takes z-index 0, under the whole stage (.stage is 1, .surface 2, .ttl 3 inside the stage, .strip 4, HUD 5). It is then occluded by the letterforms and glows through the fracture, which is the point.
- Type and colour. one family at genuinely different scales, min(7.4vw,11.4vh) headline against a .68rem/.2em uppercase mark top-left at 8vw/6.4vh and a tabular "03 / 07" bottom-right. The accent appears on the caustic, the fringe and the active tab rule, and nowhere else the shared objects do not already put it. Waterline draws no panels of its own, so 8 reaches the shared objects untouched; the only radius the format itself sets is the tab's focus ring, calc(8 * .375), inside a 2px #ffa524 outline at 5px offset.
- THE CLOSE IS DIFFERENT, the lens is removed. Slide 7 gets class "close" and --refract: 0, so for the first and only time the headline converges into one unbroken word, .f at opacity 0; it also leaves the boundary for top 50vh at min(8.2vw,12.6vh). html[data-slide="6"] fades .surface to opacity .1 over 900ms with a 300ms delay and drops .deep to .22 over 1200ms. The caustic does NOT get scaled into a hard rectangle: it glides to translateX(45vw) so its 10vw box is centred, its sliver <i> fades to opacity 0 / blur(4px), and a dedicated 84vw x 11vh <b>, radial-gradient(closest-side, color-mix(in oklab, #ffa524 52%, transparent), color-mix(in oklab, #ffa524 13%, transparent) 54%, transparent 78%), rises from scale(.26) to scale(1) at opacity .62 over 1200ms. A real soft-edged horizon bloom, clear of the strip at every size. Bullets then reveal under it at clamp(1.1rem, min(1.7vw,3.4vh), 1.85rem).
- Objects are sized for the shallow band: rings clamp(6.2rem, min(9vw,20vh), 11rem) with 15ch labels, stat values clamp(2rem, min(3.3vw,7vh), 4.2rem), the quote carousel set to --per: 3 with .quote's 40ch cap removed so three quotes fit exactly and its dots are hidden. body data-reveal-delay="520" data-animate-delay="760": the first line inks at ~540ms, while the bulk current is about 70% home, so the lower two-thirds of the frame is never left blank for a second.
- Under 760px: --surface drops to 23vh, the headline to min(11.4vw,6.6vh), the strip wraps to three rows at .6rem, the body re-anchors to top calc(var(--surface) + 15.5vh) with rings back to a wrapping flex row and the carousel back to --per: 1 with its controls restored, and the reveal drag widens to 1.6vw + --j * .9vw. html and body are overflow:hidden and .stage clips, so the nowrap headline can bleed off the right edge without ever producing a scrollbar. Under prefers-reduced-motion the whole image survives intact: the refraction, the trap and the close bloom are all resting states, not transitions, slides render pixel-identical with zero motion.
Navigation. → ↓ Space PageDown next; ← ↑ PageUp back; mouse wheel / trackpad one slide per gesture; Home/End; number keys 1–9; F for fullscreen; touch swipe. Clicking any of the seven titles in the strip jumps to that slide, with a 2px accent focus ring at 5px offset.