mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-15 07:36:50 +03:00
1895 lines
84 KiB
CSS
1895 lines
84 KiB
CSS
/* ============================================================
|
|
Screen 06: motion energy.
|
|
|
|
Three energies, and each now demonstrates itself in its own scene:
|
|
restrained and responsive carry dedicated loops further down, while the
|
|
shared scene below remains as choreographed's stage. In it, a synthetic
|
|
pointer walks a route through the wireframe and clicks two things: the
|
|
second of the two hero buttons, and a card in the gallery.
|
|
|
|
Two clicks, not four. A visitor comparing three energies is reading the
|
|
difference between them, and a scene with something happening every second
|
|
spends the visitor's attention on following the pointer instead. The two that
|
|
are left are the two where the energies actually separate: a control
|
|
answering a press, and a selection that either lands or is staged.
|
|
|
|
The whole scene runs on one 8s timeline with every beat written as a
|
|
percentage of it, and nothing is offset by animation-delay. That is what
|
|
keeps an orchestrated response in step after an hour of looping, and it is
|
|
why a frozen frame of this screen is always coherent.
|
|
|
|
The amplitude variables below still describe the shared scene's dials: how
|
|
far a press travels, whether an entrance starts from nothing, whether
|
|
siblings recede, whether a state change steps or eases. With restrained
|
|
and responsive in their own scenes, choreographed is the one energy left
|
|
turning them.
|
|
|
|
The script restarts the timeline whenever the previewed option changes. The
|
|
entrance is the loudest difference between the three, and it happens in the
|
|
first fifth of the loop: without a restart, a visitor who hovers a row four
|
|
seconds in is comparing two energies by their press depth alone.
|
|
============================================================ */
|
|
.picker-preview-motion {
|
|
--mt: 8s;
|
|
/* Ease-out-cubic, not the expo curve this started on. Expo puts four fifths
|
|
of the travel in the first fifth of the duration, so lengthening a band it
|
|
runs on buys a longer tail and not a slower arrival. */
|
|
--mt-ease: cubic-bezier(0.33, 1, 0.68, 1);
|
|
|
|
/* Restrained is the default state of every slot below: changes land whole,
|
|
nothing travels, nothing arrives. */
|
|
--mt-tf: steps(1, start);
|
|
--mt-press-scale: 1;
|
|
--mt-lift: 0px;
|
|
--mt-dim: 1;
|
|
--mt-sink: 0px;
|
|
--mt-in-o: 1;
|
|
--mt-in-y: 0px;
|
|
--mt-in-clip: 0%;
|
|
}
|
|
|
|
/* Hover previews the energy before the click commits it, same as the color
|
|
strategy rows. Motion is the one answer that does not travel to the screens
|
|
after it: a page still moving is a page you cannot judge a grid on.
|
|
|
|
All three energies now carry their own scenes further down, so the shared
|
|
scene and its amplitude variables only run in the moment before any option
|
|
is previewed or chosen. */
|
|
|
|
/* Size containment turns the frame into the coordinate system: the pointer's
|
|
route is written in cqw and cqh, so it lands on the same button at every
|
|
viewport width without a line of script. */
|
|
.picker-preview-motion .ps-desktop {
|
|
position: relative;
|
|
container-type: size;
|
|
}
|
|
|
|
/* ── The pointer ─────────────────────────────────────────────── */
|
|
|
|
/* The macOS pointer: black fill, white outline, the exact silhouette every
|
|
visitor's hand already has. Drawn as an inline SVG rather than a clip-path
|
|
because the outline is part of the shape, and it keeps its own colors on
|
|
every palette: a cursor belongs to the visitor, not to the design. One
|
|
soft shadow beneath so it sits above the page instead of printed on it,
|
|
and above every band by z-index: a hand does not pass behind the hero
|
|
image. */
|
|
.picker-preview-motion .ps-cursor {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 15px;
|
|
height: 21px;
|
|
z-index: 5;
|
|
background: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 20"%3E%3Cpath d="M1 1v15l3.9-3.4 2.4 5.6 2.6-1.1-2.4-5.6h5.1z" fill="black" stroke="white" stroke-width="1.3" stroke-linejoin="round"/%3E%3C/svg%3E') 0 0 / contain no-repeat;
|
|
filter: drop-shadow(1px 1.5px 1.5px rgb(0 0 0 / 0.3));
|
|
animation: mt-path var(--mt) ease-in-out infinite;
|
|
}
|
|
|
|
/* The click has to be visible or the state change looks unprovoked. This ring
|
|
belongs to the pointer rather than to the page, so it reads the same on all
|
|
three options: it is the visitor's hand, not the design's motion. */
|
|
.picker-preview-motion .ps-cursor::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: -4px;
|
|
left: -4px;
|
|
width: 18px;
|
|
height: 18px;
|
|
border: 1.5px solid var(--pvs-ink);
|
|
border-radius: 50%;
|
|
opacity: 0;
|
|
animation: mt-click var(--mt) linear infinite;
|
|
}
|
|
|
|
/* Parked below the page until the entrance has finished, because a pointer
|
|
moving through a page that is still arriving is two things to watch. */
|
|
@keyframes mt-path {
|
|
0%, 24% { translate: 34cqw 96cqh; }
|
|
33%, 36% { translate: 21.8cqw 45.8cqh; }
|
|
50%, 54% { translate: 73.8cqw 80.2cqh; }
|
|
70%, 100% { translate: 34cqw 96cqh; }
|
|
}
|
|
|
|
@keyframes mt-click {
|
|
0%, 33.6% { scale: 0.35; opacity: 0; }
|
|
34% { scale: 0.35; opacity: 0.55; }
|
|
36.4% { scale: 1.5; opacity: 0; }
|
|
|
|
50.6% { scale: 0.35; opacity: 0; }
|
|
51% { scale: 0.35; opacity: 0.55; }
|
|
53.4%, 100% { scale: 1.5; opacity: 0; }
|
|
}
|
|
|
|
/* ── Beat one: the hero buttons as one control, at 34% ───────── */
|
|
|
|
/* The pressed button takes the fill on the frame of the click and the one it
|
|
came from fades out behind it, which is the order that keeps a filled button
|
|
on the page throughout. Cross-fading the pair in opposite directions instead
|
|
is where the wash came from: a fill and a ghost sit a long way apart in
|
|
lightness once the strategy is drenched, primary as the ground puts the
|
|
filled state on the page's ink, and for the length of the fade the hero held
|
|
two buttons of a grey belonging to neither. */
|
|
.picker-preview-motion .ps-actions i:first-child {
|
|
animation: mt-seg-off var(--mt) var(--mt-tf) infinite;
|
|
}
|
|
|
|
.picker-preview-motion .ps-actions i:last-child {
|
|
animation:
|
|
mt-seg-on var(--mt) steps(1, start) infinite,
|
|
mt-press-seg var(--mt) linear infinite;
|
|
}
|
|
|
|
/* 120ms for the fade out, and the reset gets the same. */
|
|
@keyframes mt-seg-off {
|
|
0%, 34% { background-color: var(--pvs-cta); }
|
|
35.5%, 88% { background-color: var(--pvs-ghost); }
|
|
89.5%, 100% { background-color: var(--pvs-cta); }
|
|
}
|
|
|
|
@keyframes mt-seg-on {
|
|
0%, 34% { background-color: var(--pvs-ghost); }
|
|
35.5%, 88% { background-color: var(--pvs-cta); }
|
|
89.5%, 100% { background-color: var(--pvs-ghost); }
|
|
}
|
|
|
|
@keyframes mt-press-seg {
|
|
0%, 33.6% { scale: 1; }
|
|
34.4% { scale: var(--mt-press-scale); }
|
|
36% { scale: 1; }
|
|
100% { scale: 1; }
|
|
}
|
|
|
|
/* ── Beat two: the card at 51% ───────────────────────────────── */
|
|
|
|
/* Where the three energies separate most. All three mark the card as chosen.
|
|
Responsive adds the press. Choreographed lifts it off the page and takes its
|
|
neighbours back 80ms later, which is the difference between feedback and
|
|
something staged.
|
|
|
|
The fill starts as the chosen surface at zero alpha rather than as the
|
|
keyword: `transparent` is a black nobody asked for, and a cross-fade through
|
|
it is how a green page comes to flash a grey card. */
|
|
.picker-preview-motion .ps-gallery-item:nth-child(3) {
|
|
animation:
|
|
mt-card-on var(--mt) var(--mt-tf) infinite,
|
|
mt-card-lift var(--mt) var(--mt-tf) infinite,
|
|
mt-press-card var(--mt) linear infinite;
|
|
}
|
|
|
|
.picker-preview-motion .ps-gallery-item:not(:nth-child(3)) {
|
|
animation: mt-card-dim var(--mt) var(--mt-tf) infinite;
|
|
}
|
|
|
|
@keyframes mt-card-on {
|
|
0%, 51% {
|
|
background-color: color-mix(in oklab, var(--pvs-surface) 0%, transparent);
|
|
box-shadow: inset 0 0 0 0 var(--pvs-cta);
|
|
}
|
|
52.5%, 88% {
|
|
background-color: var(--pvs-surface);
|
|
box-shadow: inset 0 0 0 1.5px var(--pvs-cta);
|
|
}
|
|
89.5%, 100% {
|
|
background-color: color-mix(in oklab, var(--pvs-surface) 0%, transparent);
|
|
box-shadow: inset 0 0 0 0 var(--pvs-cta);
|
|
}
|
|
}
|
|
|
|
/* Lift and press are on the independent properties rather than both on
|
|
transform, so one can run on the option's curve while the other stays
|
|
linear. */
|
|
@keyframes mt-card-lift {
|
|
0%, 51% { translate: 0 0; }
|
|
53.5%, 88% { translate: 0 var(--mt-lift); }
|
|
89.5%, 100% { translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mt-press-card {
|
|
0%, 50.6% { scale: 1; }
|
|
51.4% { scale: var(--mt-press-scale); }
|
|
53% { scale: 1; }
|
|
100% { scale: 1; }
|
|
}
|
|
|
|
@keyframes mt-card-dim {
|
|
0%, 52% { filter: opacity(1); translate: 0 0; }
|
|
54%, 88% { filter: opacity(var(--mt-dim)); translate: 0 var(--mt-sink); }
|
|
89.5%, 100% { filter: opacity(1); translate: 0 0; }
|
|
}
|
|
|
|
/* ── The entrance, for the option that has one ───────────────── */
|
|
|
|
/* Six bands down the page, 200ms apart, each arriving over 560ms: 1.56s from
|
|
the first movement to the last. Slow enough to watch a band arrive and name
|
|
what arrived, and staggered widely enough to read as a sequence rather than
|
|
as one block fading in, which is the whole point of showing it. Restrained
|
|
and Responsive run the same animations with the travel set to zero, so the
|
|
page is simply already there.
|
|
|
|
The bands are containers rather than leaves. Every element that changes
|
|
state during the loop is inside one, so an entrance never has to share a
|
|
property with an interaction. */
|
|
.picker-preview-motion .ps-nav {
|
|
animation: mt-in-1 var(--mt) var(--mt-ease) infinite;
|
|
}
|
|
|
|
.picker-preview-motion .ps-hero-copy > * {
|
|
animation: mt-in-2 var(--mt) var(--mt-ease) infinite;
|
|
}
|
|
|
|
.picker-preview-motion .ps-hero > .ps-image {
|
|
animation: mt-in-image var(--mt) var(--mt-ease) infinite;
|
|
}
|
|
|
|
.picker-preview-motion .ps-proof-item {
|
|
animation: mt-in-4 var(--mt) var(--mt-ease) infinite;
|
|
}
|
|
|
|
.picker-preview-motion .ps-editorial-copy > * {
|
|
animation: mt-in-5 var(--mt) var(--mt-ease) infinite;
|
|
}
|
|
|
|
.picker-preview-motion .ps-gallery {
|
|
animation: mt-in-5 var(--mt) var(--mt-ease) infinite;
|
|
}
|
|
|
|
.picker-preview-motion .ps-footer {
|
|
animation: mt-in-6 var(--mt) var(--mt-ease) infinite;
|
|
}
|
|
|
|
@keyframes mt-in-1 {
|
|
0% { opacity: var(--mt-in-o); translate: 0 var(--mt-in-y); }
|
|
7%, 95% { opacity: 1; translate: 0 0; }
|
|
100% { opacity: var(--mt-in-o); translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mt-in-2 {
|
|
0%, 2.5% { opacity: var(--mt-in-o); translate: 0 var(--mt-in-y); }
|
|
9.5%, 95% { opacity: 1; translate: 0 0; }
|
|
100% { opacity: var(--mt-in-o); translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mt-in-4 {
|
|
0%, 7.5% { opacity: var(--mt-in-o); translate: 0 var(--mt-in-y); }
|
|
14.5%, 95% { opacity: 1; translate: 0 0; }
|
|
100% { opacity: var(--mt-in-o); translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mt-in-5 {
|
|
0%, 10% { opacity: var(--mt-in-o); translate: 0 var(--mt-in-y); }
|
|
17%, 95% { opacity: 1; translate: 0 0; }
|
|
100% { opacity: var(--mt-in-o); translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mt-in-6 {
|
|
0%, 12.5% { opacity: var(--mt-in-o); translate: 0 var(--mt-in-y); }
|
|
19.5%, 95% { opacity: 1; translate: 0 0; }
|
|
100% { opacity: var(--mt-in-o); translate: 0 0; }
|
|
}
|
|
|
|
/* The picture is the one element that arrives on its own terms: it uncovers
|
|
rather than rises, because a photograph revealing itself is the moment a
|
|
staged entrance is built around. */
|
|
@keyframes mt-in-image {
|
|
0%, 5% {
|
|
opacity: var(--mt-in-o);
|
|
clip-path: inset(0 0 var(--mt-in-clip) 0);
|
|
}
|
|
12%, 95% {
|
|
opacity: 1;
|
|
clip-path: inset(0 0 0% 0);
|
|
}
|
|
100% {
|
|
opacity: var(--mt-in-o);
|
|
clip-path: inset(0 0 var(--mt-in-clip) 0);
|
|
}
|
|
}
|
|
|
|
/* ============================================================
|
|
Restrained's own scene: a 3.8s seamless loop.
|
|
|
|
The shared 8s scene above shows restrained as the zero-amplitude case of
|
|
the other two energies. This scene replaces it whenever restrained is the
|
|
previewed option, and it demonstrates the thing restrained actually
|
|
promises: the pointer is the only element that ever travels. Every
|
|
interface response is a completed state, present on the frame the pointer
|
|
earns it and gone on the frame it stops being earned.
|
|
|
|
The route: in from the top edge to the first nav item (hover color plus a
|
|
dropdown, both instant and fully formed), across to the second (the first
|
|
resets the instant it is left, the two dropdowns never coexist), down to
|
|
the primary button (fill inverts to outline), across to the secondary
|
|
(primary restores, secondary fills), then out the way it came, with a
|
|
short idle hold so the loop has a breath in it. Every state edge below
|
|
sits on a pair of stops 0.01% apart, which under a linear timeline is a
|
|
change contained inside a single frame: nothing slides, fades, or exists
|
|
halfway.
|
|
|
|
The exit path is the entry path retraced, so 100% equals 0% and the loop
|
|
has no seam. The pointer crosses the hero copy on the way out, but only
|
|
controls answer hovers, so the page stays still. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-cursor,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-cursor {
|
|
animation: mtr-path 3.8s linear infinite;
|
|
}
|
|
|
|
/* No clicks in this scene: hovers are the only events, so the ring stays
|
|
dark. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-cursor::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-cursor::after {
|
|
animation: none;
|
|
opacity: 0;
|
|
}
|
|
|
|
/* The card beat belongs to the shared scene; this one leaves the gallery
|
|
alone. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-gallery-item,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-gallery-item {
|
|
animation: none;
|
|
}
|
|
|
|
/* ── Scene furniture shared by the per-energy loops ─────────────
|
|
The dropdown and the chevrons are drawn on every energy and idle at
|
|
opacity 0; each scene animates only the ones its script calls for, so the
|
|
scenes stay pure timelines and the geometry lives in one place.
|
|
|
|
The nav band's entrance animation makes it a stacking context, and a
|
|
stacking context without a z-index paints in DOM order, which put the
|
|
dropdown behind the hero image. Raised, so a panel opens over the picture
|
|
the way a dropdown does. */
|
|
.picker-preview-motion .ps-nav {
|
|
z-index: 3;
|
|
}
|
|
|
|
.picker-preview-motion .ps-nav-bars i:nth-child(-n + 2) {
|
|
position: relative;
|
|
}
|
|
|
|
/* The dropdown panel: a solid page-colored surface with the palette's dark
|
|
outline. The fill rides as a gradient layer clipped to the border box
|
|
because the panel opens by scaling and a background-color would too. Its
|
|
rows live on the ::before below, so an energy can move the rows without
|
|
moving the panel. */
|
|
.picker-preview-motion .ps-nav-bars i:nth-child(-n + 2)::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: calc(100% + 8px);
|
|
left: -10px;
|
|
width: 64px;
|
|
height: 54px;
|
|
box-sizing: border-box;
|
|
background-image: linear-gradient(var(--pvs-ground), var(--pvs-ground));
|
|
border: 1.5px solid var(--pvs-cta);
|
|
border-radius: var(--pvs-radius-control);
|
|
transform-origin: top center;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
}
|
|
|
|
/* The menu rows: three gradient layers over the panel's content area, the
|
|
first darker as the item the pointer would be on, ragged like a real
|
|
menu's labels. A separate box so entrances can stagger the rows against
|
|
the held panel. */
|
|
.picker-preview-motion .ps-nav-bars i:nth-child(-n + 2)::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: calc(100% + 18.5px);
|
|
left: 1.5px;
|
|
width: 41px;
|
|
height: 31px;
|
|
background-image:
|
|
linear-gradient(var(--pvs-headline), var(--pvs-headline)),
|
|
linear-gradient(var(--pvs-copy), var(--pvs-copy)),
|
|
linear-gradient(var(--pvs-copy), var(--pvs-copy));
|
|
background-repeat: no-repeat;
|
|
background-size: 78% 5px, 92% 5px, 64% 5px;
|
|
background-position: 0 0, 0 13px, 0 26px;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
z-index: 3;
|
|
}
|
|
|
|
/* The affordance chevrons: one inside the primary button, one in the lower
|
|
corner of the first card's picture. Right-pointing, drawn by clip-path,
|
|
colored for the surface each sits on. */
|
|
.picker-preview-motion .ps-actions i:first-child::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: calc(50% - 4px);
|
|
right: 12%;
|
|
width: 6px;
|
|
height: 8px;
|
|
background-color: var(--pvs-cta);
|
|
clip-path: polygon(28% 0, 100% 50%, 28% 100%, 6% 82%, 42% 50%, 6% 18%);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.picker-preview-motion .ps-actions i:first-child {
|
|
position: relative;
|
|
}
|
|
|
|
.picker-preview-motion .ps-gallery-item:nth-child(1) > i {
|
|
position: relative;
|
|
}
|
|
|
|
.picker-preview-motion .ps-gallery-item:nth-child(1) > i::after {
|
|
content: "";
|
|
position: absolute;
|
|
right: 6px;
|
|
bottom: 6px;
|
|
width: 7px;
|
|
height: 9px;
|
|
background-color: var(--pvs-ground);
|
|
clip-path: polygon(28% 0, 100% 50%, 28% 100%, 6% 82%, 42% 50%, 6% 18%);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* The first card's tint sweep: a full-cover accent layer the choreographed
|
|
finale wipes across the picture, kept under the chevron because ::after
|
|
paints later. Responsive recolors the picture itself instead; this layer
|
|
idles invisible for it. */
|
|
.picker-preview-motion .ps-gallery-item:nth-child(1) > i::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
background-color: var(--pvs-cta);
|
|
border-radius: inherit;
|
|
clip-path: inset(0 100% 0 0);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* The loop curtain: a ground-colored cover over the whole artboard that the
|
|
choreographed scene raises at the very end and lifts at the very start, so
|
|
an entrance-built loop can reset its stage without the page visibly
|
|
un-building itself. Below the cursor, which is off-frame whenever the
|
|
curtain is up. */
|
|
.picker-preview-motion .ps-desktop::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
background-color: var(--pvs-ground);
|
|
border-radius: inherit;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
z-index: 4;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-nav-bars i:nth-child(1),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-nav-bars i:nth-child(1) {
|
|
animation: mtr-nav-1 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-nav-bars i:nth-child(1)::before,
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-nav-bars i:nth-child(1)::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-nav-bars i:nth-child(1)::before,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-nav-bars i:nth-child(1)::after {
|
|
animation: mtr-drop-1 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-nav-bars i:nth-child(2),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-nav-bars i:nth-child(2) {
|
|
animation: mtr-nav-2 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-nav-bars i:nth-child(2)::before,
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-nav-bars i:nth-child(2)::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-nav-bars i:nth-child(2)::before,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-nav-bars i:nth-child(2)::after {
|
|
animation: mtr-drop-2 3.8s linear infinite;
|
|
}
|
|
|
|
/* The hero pair. The primary's hover inverts it, fill to outline; the
|
|
secondary's fills it. Both are borrowed states the buttons already own,
|
|
arriving and leaving whole. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-actions i:first-child,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-actions i:first-child {
|
|
animation: mtr-cta-primary 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion .ps-actions i:last-child,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion .ps-actions i:last-child {
|
|
animation: mtr-cta-secondary 3.8s linear infinite;
|
|
}
|
|
|
|
/* The route, in seconds of the 3.8: enter 0-0.35, first nav 0.35-0.95,
|
|
across 0.95-1.25, second nav 1.25-1.55, down 1.55-2.20, primary 2.20-2.85,
|
|
across 2.85-3.15, secondary 3.15-3.45, out 3.45-3.67, idle to 3.8.
|
|
|
|
Every stop is a custom property that plotMotionRoute() in the picker
|
|
script measures off the live layout, because the nav spaces itself in
|
|
pixels and a hardcoded fraction lands on a different bar at a different
|
|
frame width. The fallbacks are the values measured at the reference
|
|
viewport, for the pathological case of the script not having run. */
|
|
@keyframes mtr-path {
|
|
0% { translate: var(--mtr-entry, 64.5cqw -8cqh); }
|
|
9.21%, 25% { translate: var(--mtr-nav1, 64.8cqw 4.8cqh); }
|
|
32.89%, 40.79% { translate: var(--mtr-nav2, 71.4cqw 4.8cqh); }
|
|
57.89%, 75% { translate: var(--mtr-cta1, 10.3cqw 48.4cqh); }
|
|
82.89%, 90.79% { translate: var(--mtr-cta2, 20.7cqw 48.4cqh); }
|
|
96.5%, 100% { translate: var(--mtr-entry, 64.5cqw -8cqh); }
|
|
}
|
|
|
|
@keyframes mtr-nav-1 {
|
|
0%, 9.2% { background-color: var(--pvs-bars); }
|
|
9.21%, 24.99% { background-color: var(--pvs-cta); }
|
|
25%, 100% { background-color: var(--pvs-bars); }
|
|
}
|
|
|
|
@keyframes mtr-drop-1 {
|
|
0%, 9.2% { opacity: 0; }
|
|
9.21%, 24.99% { opacity: 1; }
|
|
25%, 100% { opacity: 0; }
|
|
}
|
|
|
|
@keyframes mtr-nav-2 {
|
|
0%, 32.88% { background-color: var(--pvs-bars); }
|
|
32.89%, 40.78% { background-color: var(--pvs-cta); }
|
|
40.79%, 100% { background-color: var(--pvs-bars); }
|
|
}
|
|
|
|
@keyframes mtr-drop-2 {
|
|
0%, 32.88% { opacity: 0; }
|
|
32.89%, 40.78% { opacity: 1; }
|
|
40.79%, 100% { opacity: 0; }
|
|
}
|
|
|
|
@keyframes mtr-cta-primary {
|
|
0%, 57.88% { background-color: var(--pvs-cta); box-shadow: inset 0 0 0 0 var(--pvs-cta); }
|
|
57.89%, 74.99% { background-color: var(--pvs-ghost); box-shadow: inset 0 0 0 1.5px var(--pvs-cta); }
|
|
75%, 100% { background-color: var(--pvs-cta); box-shadow: inset 0 0 0 0 var(--pvs-cta); }
|
|
}
|
|
|
|
@keyframes mtr-cta-secondary {
|
|
0%, 82.88% { background-color: var(--pvs-ghost); }
|
|
82.89%, 90.78% { background-color: var(--pvs-cta); }
|
|
90.79%, 100% { background-color: var(--pvs-ghost); }
|
|
}
|
|
|
|
/* ============================================================
|
|
Responsive's own scene: a 4.2s seamless loop.
|
|
|
|
Where restrained demonstrates completed states, this one demonstrates
|
|
answered ones: every hover gets a short, purposeful transition that
|
|
finishes and holds long enough to read. Three stations, one visit each:
|
|
the first nav item (color turn, a dropdown that expands downward with its
|
|
rows arriving on a 50ms stagger), the primary button (fill inverts to
|
|
outline and a chevron slides in 6px), and the first gallery card (picture
|
|
takes the accent tint, first label recolors, a chevron slides into the
|
|
picture's corner). Nothing bounces, nothing moves that was not hovered,
|
|
and every reset is faster than its entrance, the way real hover-out is.
|
|
|
|
In seconds of the 4.2: enter 0-0.25, dropdown opens 0.25-0.70, holds to
|
|
1.05, closes by 1.25, travel to the button by 1.45, responds by 1.80,
|
|
holds to 2.10, resets by 2.25, travel to the card by 2.55, responds by
|
|
2.85, holds to 3.50, resets by 3.70, exit and idle to 4.2. Entrances run
|
|
on cubic-bezier(0.16, 1, 0.3, 1); exits run linear and short.
|
|
|
|
The pointer leaves past the bottom edge and teleports to the top entry
|
|
point across the final 0.01% of the timeline, off-frame to off-frame, so
|
|
the loop closes without retracing the whole page. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-cursor,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-cursor {
|
|
/* ease-in-out at the animation level gives every travel segment a soft
|
|
start and arrival without a per-frame timing table. */
|
|
animation: mtv-path 4.2s ease-in-out infinite;
|
|
}
|
|
|
|
/* Hovers only, no clicks: the ring stays dark. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-cursor::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-cursor::after {
|
|
animation: none;
|
|
opacity: 0;
|
|
}
|
|
|
|
/* The shared scene's card beat and secondary-button beat are cancelled
|
|
before this scene claims the elements it needs. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-gallery-item,
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-actions i:last-child,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-gallery-item,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-actions i:last-child {
|
|
animation: none;
|
|
}
|
|
|
|
/* Station one: the nav item and its dropdown. The item turns in 120ms, the
|
|
panel scales open downward in 180ms, and the rows fade in and drop 6px
|
|
with the stagger written as intermediate background-position stops, since
|
|
the three rows are layers of one box. The close is the spec'd single
|
|
120ms gesture: rows and panel fade and retract upward together. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-nav-bars i:nth-child(1),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-nav-bars i:nth-child(1) {
|
|
animation: mtv-nav 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-nav-bars i:nth-child(1)::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-nav-bars i:nth-child(1)::after {
|
|
animation: mtv-panel 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-nav-bars i:nth-child(1)::before,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-nav-bars i:nth-child(1)::before {
|
|
animation: mtv-rows 4.2s linear infinite;
|
|
}
|
|
|
|
/* Station two: the primary button inverts over 160ms and its chevron slides
|
|
in over 200ms. Fixed box, fixed position; only fill, outline, and the
|
|
chevron move. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-actions i:first-child,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-actions i:first-child {
|
|
animation: mtv-cta 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-actions i:first-child::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-actions i:first-child::after {
|
|
animation: mtv-cta-chev 4.2s linear infinite;
|
|
}
|
|
|
|
/* Station three: the first card. Its picture tints to the palette's dark
|
|
accent over 240ms, the first label bar recolors with it, and a
|
|
page-colored chevron slides into the picture's lower corner. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-gallery-item:nth-child(1) > i,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(1) > i {
|
|
animation: mtv-card-img 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-gallery-item:nth-child(1) span b:first-child,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(1) span b:first-child {
|
|
animation: mtv-card-label 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion .ps-gallery-item:nth-child(1) > i::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(1) > i::after {
|
|
animation: mtv-card-chev 4.2s linear infinite;
|
|
}
|
|
|
|
/* Arrivals: nav at 5.95% (0.25s), button at 34.52% (1.45s), card at 60.71%
|
|
(2.55s). Departures at 25% (1.05s), 50% (2.10s), and 83.33% (3.50s). Same
|
|
measured stops as the restrained route, plus the first card. */
|
|
@keyframes mtv-path {
|
|
0% { translate: var(--mtr-entry, 64.5cqw -8cqh); }
|
|
5.95%, 25% { translate: var(--mtr-nav1, 64.8cqw 4.8cqh); }
|
|
34.52%, 50% { translate: var(--mtr-cta1, 10.3cqw 48.4cqh); }
|
|
60.71%, 83.33% { translate: var(--mtr-card1, 40cqw 80cqh); }
|
|
90%, 99.99% { translate: 32cqw 115cqh; }
|
|
100% { translate: var(--mtr-entry, 64.5cqw -8cqh); }
|
|
}
|
|
|
|
@keyframes mtv-nav {
|
|
0%, 5.95% {
|
|
background-color: var(--pvs-bars);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
8.81%, 25% { background-color: var(--pvs-cta); }
|
|
27.86%, 100% { background-color: var(--pvs-bars); }
|
|
}
|
|
|
|
/* The panel becomes visible on a single frame and does all its arriving by
|
|
scale, so "expands downward" is literal: the top edge stays pinned to the
|
|
nav item. */
|
|
@keyframes mtv-panel {
|
|
0%, 5.94% { opacity: 0; scale: 1 0; }
|
|
5.95% {
|
|
opacity: 1;
|
|
scale: 1 0;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
10.24%, 25% { opacity: 1; scale: 1 1; }
|
|
27.85% { opacity: 1; scale: 1 0; }
|
|
27.86%, 100% { opacity: 0; scale: 1 0; }
|
|
}
|
|
|
|
/* The stagger, spelled out: row one lands at 10.71% (200ms travel), rows
|
|
two and three hold their raised seats 50ms and 100ms longer and land at
|
|
11.9% and 13.1%. One box, three layers, so the offsets ride on
|
|
background-position stops instead of per-row delays. */
|
|
@keyframes mtv-rows {
|
|
0%, 5.95% { opacity: 0; background-position: 0 -6px, 0 7px, 0 20px; }
|
|
7.14% { opacity: 0.25; background-position: 0 -4.5px, 0 7px, 0 20px; }
|
|
8.33% { opacity: 0.5; background-position: 0 -3px, 0 8.5px, 0 20px; }
|
|
10.71% { opacity: 1; background-position: 0 0, 0 11.5px, 0 23px; }
|
|
11.9% { opacity: 1; background-position: 0 0, 0 13px, 0 24.5px; }
|
|
13.1%, 25% { opacity: 1; background-position: 0 0, 0 13px, 0 26px; }
|
|
27.86%, 100% { opacity: 0; background-position: 0 -6px, 0 7px, 0 20px; }
|
|
}
|
|
|
|
@keyframes mtv-cta {
|
|
0%, 34.52% {
|
|
background-color: var(--pvs-cta);
|
|
box-shadow: inset 0 0 0 0 var(--pvs-cta);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
38.33%, 50% {
|
|
background-color: var(--pvs-ghost);
|
|
box-shadow: inset 0 0 0 1.5px var(--pvs-cta);
|
|
}
|
|
52.86%, 100% {
|
|
background-color: var(--pvs-cta);
|
|
box-shadow: inset 0 0 0 0 var(--pvs-cta);
|
|
}
|
|
}
|
|
|
|
@keyframes mtv-cta-chev {
|
|
0%, 34.52% {
|
|
opacity: 0;
|
|
translate: -6px 0;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
39.29%, 50% { opacity: 1; translate: 0 0; }
|
|
52.38%, 100% { opacity: 0; translate: -6px 0; }
|
|
}
|
|
|
|
@keyframes mtv-card-img {
|
|
0%, 60.71% {
|
|
background-color: var(--pvs-accent-a);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
66.43%, 83.33% { background-color: var(--pvs-cta); }
|
|
87.14%, 100% { background-color: var(--pvs-accent-a); }
|
|
}
|
|
|
|
@keyframes mtv-card-label {
|
|
0%, 60.71% {
|
|
background-color: var(--pvs-bars);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
66.43%, 83.33% { background-color: var(--pvs-cta); }
|
|
87.14%, 100% { background-color: var(--pvs-bars); }
|
|
}
|
|
|
|
@keyframes mtv-card-chev {
|
|
0%, 60.71% {
|
|
opacity: 0;
|
|
translate: -6px 0;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
65%, 83.33% { opacity: 1; translate: 0 0; }
|
|
85.71%, 100% { opacity: 0; translate: -6px 0; }
|
|
}
|
|
|
|
/* ============================================================
|
|
Choreographed's own scene: a 5.6s directed sequence.
|
|
|
|
Restrained: the state changes. Responsive: the state transitions.
|
|
Choreographed: one transition deliberately cues the next. This scene is a
|
|
page performing its own reveal: the ground and frame are present from the
|
|
first frame, and the content arrives in a directed order, each band's
|
|
entrance handing off to the band below it.
|
|
|
|
In seconds of the 5.6: nav 0-0.45, eyebrow and headline bars wipe in
|
|
0.45-1.2, copy lines stagger 1.15-1.7, both CTAs rise as one 1.5-1.8, the
|
|
hero picture uncovers left to right 1.15-1.85 and its corner dot lands
|
|
last at 2.0. The proof row answers as one at 2.0-2.35: every dot fills
|
|
the accent together and every line extends together, a single beat rather
|
|
than a cascade, because four sequential rehearsals of the same move read
|
|
as waiting. The editorial block composes itself 2.5-3.35 (title wipe,
|
|
lines, the link dash drawing outward) while the gallery cards uncover
|
|
top-to-bottom on an 80ms stagger from 2.85. Then the finale: the cursor
|
|
enters and hovers the first card 3.8-4.8 (tint sweeps left to right,
|
|
label recolors and extends, chevron appears then travels 6px), reverses
|
|
faster on leave, and the page holds composed until the curtain resets the
|
|
loop at 5.4-5.6.
|
|
|
|
The nav dropdown and CTA hovers from the responsive scene stay available
|
|
on the page; this scene spends its seconds on page-level sequencing
|
|
instead of demonstrating them again.
|
|
|
|
Everything runs on the one 5.6s timeline as percentages, linear at the
|
|
animation level with the entrance curve applied per segment, so holds are
|
|
exact and a restart is always coherent. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion :is(.ps-hero-copy > *, .ps-proof-item, .ps-editorial-copy > *, .ps-gallery, .ps-gallery-item, .ps-actions i),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion :is(.ps-hero-copy > *, .ps-proof-item, .ps-editorial-copy > *, .ps-gallery, .ps-gallery-item, .ps-actions i) {
|
|
/* The shared scene's slots are cleared first; this scene animates leaves,
|
|
not bands. */
|
|
animation: none;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-cursor,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-cursor {
|
|
animation: mtc-path 5.6s ease-in-out infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-cursor::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-cursor::after {
|
|
animation: none;
|
|
opacity: 0;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-desktop::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-desktop::after {
|
|
animation: mtc-curtain 5.6s linear infinite;
|
|
}
|
|
|
|
/* ── The overture: nav, then type, then picture ──────────────── */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-nav,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-nav {
|
|
animation: mtc-nav 5.6s linear infinite;
|
|
}
|
|
|
|
/* Child-combinator form, because the shared-scene reset above carries
|
|
:is()'s worst-argument specificity and a bare .ps-eyebrow loses to it. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-hero-copy > .ps-eyebrow,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-hero-copy > .ps-eyebrow {
|
|
animation: mtc-eyebrow 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-headline i:nth-child(1),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-headline i:nth-child(1) {
|
|
animation: mtc-headline-1 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-headline i:nth-child(2),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-headline i:nth-child(2) {
|
|
animation: mtc-headline-2 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-copy i:nth-child(1),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-copy i:nth-child(1) {
|
|
animation: mtc-copy-1 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-copy i:nth-child(2),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-copy i:nth-child(2) {
|
|
animation: mtc-copy-2 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-copy i:nth-child(3),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-copy i:nth-child(3) {
|
|
animation: mtc-copy-3 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-hero-copy > .ps-actions,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-hero-copy > .ps-actions {
|
|
animation: mtc-actions 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-hero > .ps-image,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-hero > .ps-image {
|
|
animation: mtc-image 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-hero > .ps-image::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-hero > .ps-image::after {
|
|
animation: mtc-dot 5.6s linear infinite;
|
|
}
|
|
|
|
/* ── The scroll handoff: dots fill, lines extend, one beat ────── */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-proof-item i,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-proof-item i {
|
|
animation: mtc-pdot 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-proof-item span,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-proof-item span {
|
|
transform-origin: left center;
|
|
animation: mtc-pline 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-proof-divider,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-proof-divider {
|
|
animation: mtc-pdiv 5.6s linear infinite;
|
|
}
|
|
|
|
/* ── The editorial block composes itself ─────────────────────── */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-editorial-copy strong,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-editorial-copy strong {
|
|
animation: mtc-ed-title 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-editorial-copy span i:nth-child(1),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-editorial-copy span i:nth-child(1) {
|
|
animation: mtc-ed-line-1 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-editorial-copy span i:nth-child(2),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-editorial-copy span i:nth-child(2) {
|
|
animation: mtc-ed-line-2 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-editorial-copy > em,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-editorial-copy > em {
|
|
transform-origin: left center;
|
|
animation: mtc-ed-dash 5.6s linear infinite;
|
|
}
|
|
|
|
/* ── The gallery cards, overlapping left to right ────────────── */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(1) > i,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(1) > i {
|
|
animation: mtc-g-img-1 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(2) > i,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(2) > i {
|
|
animation: mtc-g-img-2 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(3) > i,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(3) > i {
|
|
animation: mtc-g-img-3 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(4) > i,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(4) > i {
|
|
animation: mtc-g-img-4 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(1) span,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(1) span {
|
|
animation: mtc-g-lab-1 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(2) span,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(2) span {
|
|
animation: mtc-g-lab-2 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(3) span,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(3) span {
|
|
animation: mtc-g-lab-3 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(4) span,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(4) span {
|
|
animation: mtc-g-lab-4 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-footer,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-footer {
|
|
animation: mtc-footer 5.6s linear infinite;
|
|
}
|
|
|
|
/* ── The finale: the first card answers a hover in order ─────── */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(1) > i::before,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(1) > i::before {
|
|
animation: mtc-tint 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(1) span b:first-child,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(1) span b:first-child {
|
|
animation: mtc-label 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion .ps-gallery-item:nth-child(1) > i::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion .ps-gallery-item:nth-child(1) > i::after {
|
|
animation: mtc-chev 5.6s linear infinite;
|
|
}
|
|
|
|
/* The curtain is up across the loop's wrap, so the composed page never
|
|
visibly un-builds: it dissolves to ground at 5.4s and the ground lifts
|
|
onto the arriving nav at 0.26s. */
|
|
@keyframes mtc-curtain {
|
|
0% { opacity: 1; }
|
|
4.6% { opacity: 0; }
|
|
96.4% { opacity: 0; }
|
|
100% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes mtc-nav {
|
|
0%, 1.7% {
|
|
opacity: 0;
|
|
translate: 0 8px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
8%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-eyebrow {
|
|
0%, 8% {
|
|
clip-path: inset(0 100% 0 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
13.4%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-headline-1 {
|
|
0%, 11.6% {
|
|
clip-path: inset(0 100% 0 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
17.9%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-headline-2 {
|
|
0%, 15.2% {
|
|
clip-path: inset(0 100% 0 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
21.4%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-copy-1 {
|
|
0%, 20.5% {
|
|
opacity: 0;
|
|
translate: 0 6px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
25.9%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-copy-2 {
|
|
0%, 22.7% {
|
|
opacity: 0;
|
|
translate: 0 6px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
28%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-copy-3 {
|
|
0%, 24.8% {
|
|
opacity: 0;
|
|
translate: 0 6px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
30.2%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-actions {
|
|
0%, 26.8% {
|
|
opacity: 0;
|
|
translate: 0 6px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
32.1%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-image {
|
|
0%, 20.5% {
|
|
clip-path: inset(0 100% 0 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
33%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-dot {
|
|
0%, 33% {
|
|
opacity: 0;
|
|
scale: 0.4;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
35.7%, 100% { opacity: 1; scale: 1; }
|
|
}
|
|
|
|
/* All four dots fill together at 2.0s and all four lines extend together a
|
|
breath behind them: one answered beat for the whole row. */
|
|
@keyframes mtc-pdot {
|
|
0%, 35.7% {
|
|
opacity: 0;
|
|
scale: 0.6;
|
|
background-color: var(--pvs-accent-a);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
38.6%, 100% { opacity: 1; scale: 1; background-color: var(--pvs-cta); }
|
|
}
|
|
|
|
@keyframes mtc-pline {
|
|
0%, 36.8% {
|
|
scale: 0 1;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
41.8%, 100% { scale: 1 1; }
|
|
}
|
|
|
|
@keyframes mtc-pdiv {
|
|
0%, 35.7% { opacity: 0; }
|
|
38.9%, 100% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes mtc-ed-title {
|
|
0%, 44.6% {
|
|
clip-path: inset(0 100% 0 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
50%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-ed-line-1 {
|
|
0%, 49.1% {
|
|
opacity: 0;
|
|
translate: 0 5px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
54.5%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-ed-line-2 {
|
|
0%, 50.9% {
|
|
opacity: 0;
|
|
translate: 0 5px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
56.3%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-ed-dash {
|
|
0%, 55.4% {
|
|
scale: 0 1;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
59.8%, 100% { scale: 1 1; }
|
|
}
|
|
|
|
/* The cards, 80ms apart from 2.85s, each picture uncovering top to bottom
|
|
with its labels arriving 200ms behind it: the next card is already moving
|
|
before the previous one settles. */
|
|
@keyframes mtc-g-img-1 {
|
|
0%, 50.9% {
|
|
clip-path: inset(0 0 100% 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
57.3%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-g-img-2 {
|
|
0%, 52.3% {
|
|
clip-path: inset(0 0 100% 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
58.8%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-g-img-3 {
|
|
0%, 53.8% {
|
|
clip-path: inset(0 0 100% 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
60.2%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-g-img-4 {
|
|
0%, 55.2% {
|
|
clip-path: inset(0 0 100% 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
61.6%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-g-lab-1 {
|
|
0%, 54.5% {
|
|
opacity: 0;
|
|
translate: 0 5px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
59.8%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-g-lab-2 {
|
|
0%, 55.9% {
|
|
opacity: 0;
|
|
translate: 0 5px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
61.3%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-g-lab-3 {
|
|
0%, 57.3% {
|
|
opacity: 0;
|
|
translate: 0 5px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
62.7%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-g-lab-4 {
|
|
0%, 58.8% {
|
|
opacity: 0;
|
|
translate: 0 5px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
64.1%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mtc-footer {
|
|
0%, 58.9% { opacity: 0; }
|
|
64.3%, 100% { opacity: 1; }
|
|
}
|
|
|
|
/* The cursor waits below the frame until the page has finished building,
|
|
rises to the first card at 3.8s, holds through the card's answer, and is
|
|
gone again before the curtain: at the wrap both endpoints are off-frame,
|
|
so the loop closes clean. */
|
|
@keyframes mtc-path {
|
|
0%, 60.7% { translate: 45cqw 115cqh; }
|
|
67.9%, 85.7% { translate: var(--mtr-card1, 40cqw 80cqh); }
|
|
92%, 100% { translate: 45cqw 115cqh; }
|
|
}
|
|
|
|
@keyframes mtc-tint {
|
|
0%, 67.9% { opacity: 0; clip-path: inset(0 100% 0 0); }
|
|
67.91% {
|
|
opacity: 1;
|
|
clip-path: inset(0 100% 0 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
72.1%, 85.7% { opacity: 1; clip-path: inset(0 0 0 0); }
|
|
88.6%, 100% { opacity: 0; clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mtc-label {
|
|
0%, 71.4% {
|
|
background-color: var(--pvs-bars);
|
|
width: 76%;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
75.9%, 85.7% { background-color: var(--pvs-cta); width: 84%; }
|
|
88.6%, 100% { background-color: var(--pvs-bars); width: 76%; }
|
|
}
|
|
|
|
@keyframes mtc-chev {
|
|
0%, 73.2% { opacity: 0; translate: -6px 0; }
|
|
75.9% {
|
|
opacity: 1;
|
|
translate: -6px 0;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
79.5%, 85.7% { opacity: 1; translate: 0 0; }
|
|
87.5%, 100% { opacity: 0; translate: 0 0; }
|
|
}
|
|
|
|
/* ============================================================
|
|
The portfolio board, and its three scenes.
|
|
|
|
A landing page and a portfolio spend movement on different things, so the
|
|
two surfaces this question is put to are shown as two pages rather than as
|
|
one page relabelled. This board is the index the font screen already draws,
|
|
in bars: a page title, two staggered entries with a caption ladder beside
|
|
each, and the carousel rail under them.
|
|
|
|
Everything the two boards have in common is drawn once. The frame, the nav
|
|
and its dropdown, the pointer, the click ring, and the loop curtain all key
|
|
on the class both boards carry, so the nav station of every scene, the
|
|
curtain, and the nav's own entrance arrive here already written. What
|
|
follows is the part a portfolio does differently: the work is the interface,
|
|
so the stations that are not chrome are plates, captions, and the carousel.
|
|
|
|
The three scenes keep the landing page's timings to the frame, because the
|
|
two are compared by moving between tabs on one screen and a portfolio that
|
|
ran to a different clock would read as a different answer. Restrained keeps
|
|
its four stations and its 3.8s, responsive its three and its 4.2s,
|
|
choreographed its 5.6s reveal.
|
|
|
|
These rules are no more specific than the landing page's and come after
|
|
them, so source order is what settles the elements the two boards share.
|
|
Anything added here has to stay after them.
|
|
============================================================ */
|
|
.picker-preview-motion--index {
|
|
/* Three bands rather than five: the nav, the index, the footer. */
|
|
--pvs-rows: 9.4% minmax(0, 1fr) 8.3%;
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index {
|
|
gap: 13px;
|
|
padding: 2.4% var(--pvs-gutter-end) 2.4% var(--pvs-gutter);
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-head {
|
|
display: block;
|
|
width: 26%;
|
|
height: 13px;
|
|
background-color: var(--pvs-headline);
|
|
border-radius: var(--pvs-radius-bar);
|
|
}
|
|
|
|
/* The plate clips because the caption band the scenes bring up starts below
|
|
its bottom edge. */
|
|
.picker-preview-motion--index .ps-index-row > .ps-image {
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* The view state: a caption band rising over the foot of the plate, which is
|
|
what a portfolio index answers a pointer with. The plate keeps its own
|
|
radius on the two corners it shares with the band. */
|
|
.picker-preview-motion--index .ps-index-row > .ps-image::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: auto 0 0;
|
|
height: 26%;
|
|
background-color: var(--pvs-cta);
|
|
border-radius: 0 0 var(--pvs-radius-surface) var(--pvs-radius-surface);
|
|
opacity: 0;
|
|
translate: 0 100%;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-cap {
|
|
gap: 9px;
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-name {
|
|
display: block;
|
|
width: 66%;
|
|
height: 11px;
|
|
background-color: var(--pvs-title);
|
|
border-radius: var(--pvs-radius-bar);
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-tag {
|
|
display: block;
|
|
width: 32%;
|
|
height: 5px;
|
|
background-color: var(--pvs-bars);
|
|
border-radius: var(--pvs-radius-bar);
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-lines {
|
|
display: grid;
|
|
gap: 7px;
|
|
margin-top: 3px;
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-lines b {
|
|
display: block;
|
|
width: 76%;
|
|
height: 6px;
|
|
background-color: var(--pvs-copy);
|
|
border-radius: var(--pvs-radius-bar);
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-lines b:last-child {
|
|
width: 54%;
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-arrow {
|
|
width: 7px;
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-track {
|
|
align-items: center;
|
|
gap: 13px;
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-track b {
|
|
display: block;
|
|
width: 22px;
|
|
height: 5px;
|
|
background-color: var(--pvs-bars);
|
|
border-radius: var(--pvs-radius-bar);
|
|
}
|
|
|
|
/* The track is in the selector so the current stop outranks the row it sits
|
|
in, which is scoped one element deeper. */
|
|
.picker-preview-motion--index .ps-index-track .ps-index-stop--on {
|
|
background-color: var(--pvs-cta);
|
|
}
|
|
|
|
/* ── Restrained on the index: four stations, no travel but the pointer ──
|
|
The two nav items are the landing page's own first two stations and arrive
|
|
with them. The two that follow are plates: the caption band is present on
|
|
the frame the pointer earns it and gone on the frame it leaves, the title
|
|
takes the accent with it, and the plate's corner mark answers as well. The
|
|
first plate resets the instant the pointer is off it, so no two entries are
|
|
ever both showing their caption. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion--index .ps-cursor,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion--index .ps-cursor {
|
|
animation: mxr-path 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::before,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::before {
|
|
animation: mxr-band-1 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) .ps-index-name,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) .ps-index-name {
|
|
animation: mxr-name-1 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::after {
|
|
animation: mxr-mark-1 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion--index .ps-index-row:nth-of-type(2) > .ps-image::before,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(2) > .ps-image::before {
|
|
animation: mxr-band-2 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion--index .ps-index-row:nth-of-type(2) .ps-index-name,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(2) .ps-index-name {
|
|
animation: mxr-name-2 3.8s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="restrained"]) .picker-preview-motion--index .ps-index-row:nth-of-type(2) > .ps-image::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="restrained"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(2) > .ps-image::after {
|
|
animation: mxr-mark-2 3.8s linear infinite;
|
|
}
|
|
|
|
/* The landing page's own restrained stops, with the two plates standing in for
|
|
its two buttons: in 0-0.35, first nav 0.35-0.95, second 1.25-1.55, first
|
|
plate 2.20-2.85, second 3.15-3.45, out by 3.67. */
|
|
@keyframes mxr-path {
|
|
0% { translate: var(--mtr-entry, 64.5cqw -8cqh); }
|
|
9.21%, 25% { translate: var(--mtr-nav1, 64.8cqw 4.8cqh); }
|
|
32.89%, 40.79% { translate: var(--mtr-nav2, 71.4cqw 4.8cqh); }
|
|
57.89%, 75% { translate: var(--mxi-work1, 25.6cqw 33.3cqh); }
|
|
82.89%, 90.79% { translate: var(--mxi-work2, 74.4cqw 60.9cqh); }
|
|
96.5%, 100% { translate: var(--mtr-entry, 64.5cqw -8cqh); }
|
|
}
|
|
|
|
@keyframes mxr-band-1 {
|
|
0%, 57.88% { opacity: 0; translate: 0 100%; }
|
|
57.89%, 74.99% { opacity: 1; translate: 0 0; }
|
|
75%, 100% { opacity: 0; translate: 0 100%; }
|
|
}
|
|
|
|
@keyframes mxr-name-1 {
|
|
0%, 57.88% { background-color: var(--pvs-title); }
|
|
57.89%, 74.99% { background-color: var(--pvs-cta); }
|
|
75%, 100% { background-color: var(--pvs-title); }
|
|
}
|
|
|
|
@keyframes mxr-mark-1 {
|
|
0%, 57.88% { background-color: var(--pvs-accent-d); scale: 1; }
|
|
57.89%, 74.99% { background-color: var(--pvs-cta); scale: 1.4; }
|
|
75%, 100% { background-color: var(--pvs-accent-d); scale: 1; }
|
|
}
|
|
|
|
@keyframes mxr-band-2 {
|
|
0%, 82.88% { opacity: 0; translate: 0 100%; }
|
|
82.89%, 90.78% { opacity: 1; translate: 0 0; }
|
|
90.79%, 100% { opacity: 0; translate: 0 100%; }
|
|
}
|
|
|
|
@keyframes mxr-name-2 {
|
|
0%, 82.88% { background-color: var(--pvs-title); }
|
|
82.89%, 90.78% { background-color: var(--pvs-cta); }
|
|
90.79%, 100% { background-color: var(--pvs-title); }
|
|
}
|
|
|
|
@keyframes mxr-mark-2 {
|
|
0%, 82.88% { background-color: var(--pvs-accent-d); scale: 1; }
|
|
82.89%, 90.78% { background-color: var(--pvs-cta); scale: 1.4; }
|
|
90.79%, 100% { background-color: var(--pvs-accent-d); scale: 1; }
|
|
}
|
|
|
|
/* ── Responsive on the index: three stations, each answered ──
|
|
The nav station and its staggered dropdown are the landing page's and run
|
|
here unchanged. The second is the first plate: the caption band rises over
|
|
its foot in 200ms, the title recolors and the corner mark answers with it.
|
|
The third is the carousel, which is the one control only this board has: the
|
|
next arrow turns, the track shifts under it, and the stop that was current
|
|
hands over to the next one.
|
|
|
|
A carousel that snapped back the moment the pointer left would be reporting
|
|
a state nobody put it in, so the advance is the one thing here that stays.
|
|
It returns across the single frame the pointer teleports off-frame on, which
|
|
is also what makes 100% equal 0% and the loop seamless. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion--index .ps-cursor,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion--index .ps-cursor {
|
|
animation: mxv-path 4.2s ease-in-out infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::before,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::before {
|
|
animation: mxv-band 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) .ps-index-name,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) .ps-index-name {
|
|
animation: mxv-name 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::after {
|
|
animation: mxv-mark 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion--index .ps-index-arrow--next,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion--index .ps-index-arrow--next {
|
|
animation: mxv-arrow 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion--index .ps-index-track,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion--index .ps-index-track {
|
|
animation: mxv-track 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion--index .ps-index-track b:nth-child(1),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion--index .ps-index-track b:nth-child(1) {
|
|
animation: mxv-stop-off 4.2s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="responsive"]) .picker-preview-motion--index .ps-index-track b:nth-child(2),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="responsive"]:checked) .picker-preview-motion--index .ps-index-track b:nth-child(2) {
|
|
animation: mxv-stop-on 4.2s linear infinite;
|
|
}
|
|
|
|
/* Arrivals: nav at 5.95% (0.25s), plate at 34.52% (1.45s), arrow at 60.71%
|
|
(2.55s). Departures at 25%, 50%, and 83.33%, the landing page's own. */
|
|
@keyframes mxv-path {
|
|
0% { translate: var(--mtr-entry, 64.5cqw -8cqh); }
|
|
5.95%, 25% { translate: var(--mtr-nav1, 64.8cqw 4.8cqh); }
|
|
34.52%, 50% { translate: var(--mxi-work1, 25.6cqw 33.3cqh); }
|
|
60.71%, 83.33% { translate: var(--mxi-rail, 92.6cqw 87.9cqh); }
|
|
90%, 99.99% { translate: 32cqw 115cqh; }
|
|
100% { translate: var(--mtr-entry, 64.5cqw -8cqh); }
|
|
}
|
|
|
|
@keyframes mxv-band {
|
|
0%, 34.52% {
|
|
opacity: 0;
|
|
translate: 0 100%;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
39.29%, 50% { opacity: 1; translate: 0 0; }
|
|
52.86%, 100% { opacity: 0; translate: 0 100%; }
|
|
}
|
|
|
|
@keyframes mxv-name {
|
|
0%, 34.52% {
|
|
background-color: var(--pvs-title);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
38.33%, 50% { background-color: var(--pvs-cta); }
|
|
52.86%, 100% { background-color: var(--pvs-title); }
|
|
}
|
|
|
|
@keyframes mxv-mark {
|
|
0%, 34.52% {
|
|
background-color: var(--pvs-accent-d);
|
|
scale: 1;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
39.29%, 50% { background-color: var(--pvs-cta); scale: 1.4; }
|
|
52.86%, 100% { background-color: var(--pvs-accent-d); scale: 1; }
|
|
}
|
|
|
|
@keyframes mxv-arrow {
|
|
0%, 60.71% {
|
|
border-color: var(--pvs-bars);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
63.57%, 83.33% { border-color: var(--pvs-cta); }
|
|
86.19%, 100% { border-color: var(--pvs-bars); }
|
|
}
|
|
|
|
@keyframes mxv-track {
|
|
0%, 61.9% {
|
|
translate: 0 0;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
67.62%, 99.99% { translate: -6px 0; }
|
|
100% { translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mxv-stop-off {
|
|
0%, 61.9% {
|
|
background-color: var(--pvs-cta);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
65.71%, 99.99% { background-color: var(--pvs-bars); }
|
|
100% { background-color: var(--pvs-cta); }
|
|
}
|
|
|
|
@keyframes mxv-stop-on {
|
|
0%, 61.9% {
|
|
background-color: var(--pvs-bars);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
65.71%, 99.99% { background-color: var(--pvs-cta); }
|
|
100% { background-color: var(--pvs-bars); }
|
|
}
|
|
|
|
/* ── Choreographed on the index: the index hangs itself ──
|
|
The curtain, the nav's arrival, and the footer's are the landing page's and
|
|
run here as they are. What this board sequences is an index being hung: the
|
|
page title wipes in, the first plate uncovers left to right and its caption
|
|
composes after it, the second plate uncovers from the other side because the
|
|
row is flipped and a reveal that ignored the flip would run away from the
|
|
caption it belongs to, then the carousel's arrows and stops arrive on an
|
|
80ms walk. The finale is the pointer coming up to the first entry, which is
|
|
the same hover the responsive scene demonstrates, arriving as the last beat
|
|
of a sequence instead of as an answer.
|
|
|
|
In seconds of the 5.6: title 0.45-1.05, first plate 0.9-1.7 with its caption
|
|
1.5-2.0, second plate 1.9-2.7 with its caption 2.5-3.0, arrows 3.1-3.4,
|
|
stops 3.2-3.6, footer 3.3-3.6, the hover 3.9-4.9, curtain 5.4-5.6.
|
|
|
|
The caption reveals sit on the ladder rather than on its bars, because the
|
|
title bar spends its own keyframes on the finale and an element takes one
|
|
animation per property. */
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-cursor,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-cursor {
|
|
animation: mxc-path 5.6s ease-in-out infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-head,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-head {
|
|
animation: mxc-head 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image {
|
|
animation: mxc-plate-1 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-row:nth-of-type(2) > .ps-image,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(2) > .ps-image {
|
|
animation: mxc-plate-2 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) .ps-index-cap,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) .ps-index-cap {
|
|
animation: mxc-cap-1 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-row:nth-of-type(2) .ps-index-cap,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(2) .ps-index-cap {
|
|
animation: mxc-cap-2 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-arrow,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-arrow {
|
|
animation: mxc-arrow 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-track b:nth-child(1),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-track b:nth-child(1) {
|
|
animation: mxc-stop-1 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-track b:nth-child(2),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-track b:nth-child(2) {
|
|
animation: mxc-stop-2 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-track b:nth-child(3),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-track b:nth-child(3) {
|
|
animation: mxc-stop-3 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-track b:nth-child(4),
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-track b:nth-child(4) {
|
|
animation: mxc-stop-4 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::before,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::before {
|
|
animation: mxc-band 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) .ps-index-name,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) .ps-index-name {
|
|
animation: mxc-name 5.6s linear infinite;
|
|
}
|
|
|
|
#picker-form:has(.picker-strategy-option:hover input[name="motion-energy"][value="choreographed"]) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::after,
|
|
#picker-form:not(:has(.picker-strategy-option:hover input[name="motion-energy"])):has(input[name="motion-energy"][value="choreographed"]:checked) .picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::after {
|
|
animation: mxc-mark 5.6s linear infinite;
|
|
}
|
|
|
|
/* The pointer waits below the frame until the index is hung, comes up to the
|
|
first entry at 3.9s, and is gone again before the curtain, so both ends of
|
|
the loop are off-frame. */
|
|
@keyframes mxc-path {
|
|
0%, 62.5% { translate: 45cqw 115cqh; }
|
|
69.64%, 87.5% { translate: var(--mxi-work1, 25.6cqw 33.3cqh); }
|
|
93%, 100% { translate: 45cqw 115cqh; }
|
|
}
|
|
|
|
@keyframes mxc-head {
|
|
0%, 8.04% {
|
|
clip-path: inset(0 100% 0 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
18.75%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mxc-plate-1 {
|
|
0%, 16.07% {
|
|
clip-path: inset(0 100% 0 0);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
30.36%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mxc-plate-2 {
|
|
0%, 33.93% {
|
|
clip-path: inset(0 0 0 100%);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
48.21%, 100% { clip-path: inset(0 0 0 0); }
|
|
}
|
|
|
|
@keyframes mxc-cap-1 {
|
|
0%, 26.79% {
|
|
opacity: 0;
|
|
translate: 0 6px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
35.71%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mxc-cap-2 {
|
|
0%, 44.64% {
|
|
opacity: 0;
|
|
translate: 0 6px;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
53.57%, 100% { opacity: 1; translate: 0 0; }
|
|
}
|
|
|
|
@keyframes mxc-arrow {
|
|
0%, 55.36% { opacity: 0; }
|
|
60.71%, 100% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes mxc-stop-1 {
|
|
0%, 57.14% { opacity: 0; }
|
|
62.5%, 100% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes mxc-stop-2 {
|
|
0%, 58.57% { opacity: 0; }
|
|
63.93%, 100% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes mxc-stop-3 {
|
|
0%, 60% { opacity: 0; }
|
|
65.36%, 100% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes mxc-stop-4 {
|
|
0%, 61.43% { opacity: 0; }
|
|
66.79%, 100% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes mxc-band {
|
|
0%, 69.64% {
|
|
opacity: 0;
|
|
translate: 0 100%;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
73.21%, 87.5% { opacity: 1; translate: 0 0; }
|
|
89.29%, 100% { opacity: 0; translate: 0 100%; }
|
|
}
|
|
|
|
/* One animation for two beats: the title's own reveal, then the accent and the
|
|
extra measure it takes when the pointer arrives at the end of the scene. */
|
|
@keyframes mxc-name {
|
|
0%, 26.79% {
|
|
clip-path: inset(0 100% 0 0);
|
|
width: 66%;
|
|
background-color: var(--pvs-title);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
33.93%, 70.54% {
|
|
clip-path: inset(0 0 0 0);
|
|
width: 66%;
|
|
background-color: var(--pvs-title);
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
75%, 87.5% { clip-path: inset(0 0 0 0); width: 74%; background-color: var(--pvs-cta); }
|
|
89.29%, 100% { clip-path: inset(0 0 0 0); width: 66%; background-color: var(--pvs-title); }
|
|
}
|
|
|
|
@keyframes mxc-mark {
|
|
0%, 70.54% {
|
|
background-color: var(--pvs-accent-d);
|
|
scale: 1;
|
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
74.11%, 87.5% { background-color: var(--pvs-cta); scale: 1.4; }
|
|
89.29%, 100% { background-color: var(--pvs-accent-d); scale: 1; }
|
|
}
|
|
|
|
/* A loop nobody asked for is the definition of a nonessential one. Reduced
|
|
motion gets the end of the scene instead: the pointer parked over the card
|
|
it chose, with every state already changed, which is the same information
|
|
without the movement. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
/* Pseudo-elements are not valid inside :is() and get dropped from the list
|
|
rather than failing loudly, so the one that animates is cancelled on its
|
|
own line. */
|
|
.picker-preview-motion .ps-cursor::after,
|
|
.picker-preview-motion .ps-nav-bars i::before,
|
|
.picker-preview-motion .ps-nav-bars i::after,
|
|
.picker-preview-motion .ps-actions i::after,
|
|
.picker-preview-motion .ps-gallery-item > i::before,
|
|
.picker-preview-motion .ps-gallery-item > i::after,
|
|
.picker-preview-motion .ps-image::before,
|
|
.picker-preview-motion .ps-image::after,
|
|
.picker-preview-motion .ps-desktop::after,
|
|
.picker-preview-motion :is(
|
|
.ps-cursor,
|
|
.ps-nav,
|
|
.ps-nav-bars i,
|
|
.ps-index-head,
|
|
.ps-index-cap,
|
|
.ps-index-name,
|
|
.ps-index-arrow,
|
|
.ps-index-track,
|
|
.ps-index-track b,
|
|
.ps-hero-copy > *,
|
|
.ps-eyebrow,
|
|
.ps-headline i,
|
|
.ps-copy i,
|
|
.ps-image,
|
|
.ps-actions i,
|
|
.ps-proof-item,
|
|
.ps-proof-item i,
|
|
.ps-proof-item span,
|
|
.ps-proof-divider,
|
|
.ps-editorial-copy > *,
|
|
.ps-editorial-copy span i,
|
|
.ps-gallery,
|
|
.ps-gallery-item,
|
|
.ps-gallery-item > i,
|
|
.ps-gallery-item span,
|
|
.ps-gallery-item b,
|
|
.ps-footer
|
|
) {
|
|
/* The declarations being cancelled are per-element and more specific than
|
|
any list can be without repeating all ten selectors. A preference the
|
|
visitor set in their operating system outranks them. */
|
|
animation: none !important;
|
|
}
|
|
|
|
.picker-preview-motion .ps-cursor {
|
|
translate: 73.8cqw 80.2cqh;
|
|
}
|
|
|
|
.picker-preview-motion .ps-actions i:first-child {
|
|
background-color: var(--pvs-ghost);
|
|
}
|
|
|
|
.picker-preview-motion .ps-actions i:last-child {
|
|
background-color: var(--pvs-cta);
|
|
}
|
|
|
|
.picker-preview-motion .ps-gallery-item:nth-child(3) {
|
|
background-color: var(--pvs-surface);
|
|
box-shadow: inset 0 0 0 1.5px var(--pvs-cta);
|
|
translate: 0 var(--mt-lift);
|
|
}
|
|
|
|
.picker-preview-motion .ps-gallery-item:not(:nth-child(3)) {
|
|
filter: opacity(var(--mt-dim));
|
|
translate: 0 var(--mt-sink);
|
|
}
|
|
|
|
/* The portfolio board's own resting frame: the pointer on the first entry
|
|
with that entry's caption band up, which is where all three of its scenes
|
|
end up. */
|
|
.picker-preview-motion--index .ps-cursor {
|
|
translate: var(--mxi-work1, 25.6cqw 33.3cqh);
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::before {
|
|
opacity: 1;
|
|
translate: 0 0;
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-row:nth-of-type(1) .ps-index-name {
|
|
background-color: var(--pvs-cta);
|
|
}
|
|
|
|
.picker-preview-motion--index .ps-index-row:nth-of-type(1) > .ps-image::after {
|
|
background-color: var(--pvs-cta);
|
|
scale: 1.4;
|
|
}
|
|
}
|