
@font-face {
  font-family: 'Grotesk';
  src: url('/space-grotesk.woff2') format('woff2');
  font-weight: 300 700;
  font-display: swap;
}

:root {
  --paper:      #F1F3F1;
  --sheet:      #FFFFFF;
  --ink:        #12201B;
  --ink-2:      #46554E;
  --ink-3:      #5B6A63;
  --rule:       #D2DAD5;
  --pine:       #0F3B2F;
  --pine-deep:  #0A2A22;
  --pine-tint:  #DFE9E3;
  /* HEX, never rgba(). cssTokens() in check-contrast.mjs matches a hex literal
     and is structurally blind to an alpha, so a ground written as rgba is a
     ground the checker cannot see — and an undeclared pair reports as nothing
     rather than as a failure. That is the exact mechanism that let the hero
     eyebrow paint 2.19:1 through a green CI run. */
  --tex:        #E6EAE7;
  --pine-lit:   #20493E;

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 72px; --s9: 104px;

  --sans: 'Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;
  --measure: 34rem;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 18px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* Colour and background are always set together. Five separate bugs in this
   repo came from a generic rule setting one and a component overriding only
   the other — a seat label painted paper on paper, a tab that ate its own
   label on hover. */
a { color: inherit; text-decoration: none; }
:focus-visible { outline: 3px solid var(--pine); outline-offset: 3px; border-radius: 2px; }
.on-pine :focus-visible { outline-color: var(--pine-tint); }

/* Every band nests its own .wrap. A padding SHORTHAND on a class beats an
   element rule, so a section rule setting vertical padding plus a .wrap rule
   setting "0 var(--s5)" on the SAME element silently zeroes the rhythm — which
   is how four sections lost 224px each in the previous version. */
.wrap { max-width: 70rem; margin: 0 auto; padding: 0 var(--s5); }

/* Off-screen until focused. Every keyboard user currently tabs the whole
   masthead before reaching a word of the page. */
.skip {
  position: absolute; left: -9999px;
  /* Pine ground, not sheet. It was white on a --paper band: 1.12:1, which the
     in-page sweep measures as a control boundary and which a focused user
     would have seen as a white shape on a near-white page. Off-screen is not
     an exemption — the element is painted the moment it takes focus. */
  background: var(--pine); color: var(--sheet);
  padding: var(--s3) var(--s5); border-radius: 999px; font-weight: 600;
}
.skip:focus { left: var(--s5); top: var(--s3); z-index: 10; }
.band { padding: var(--s9) 0; }
/* With every band weighted identically, nothing on the page is important.
   Exactly two carry this: the one claim worth stopping for, and the move. */
.band.heavy { padding: 160px 0; }
.band.heavy .wrap { max-width: 52rem; }
.band.heavy h2 { font-size: clamp(2.2rem, 4.6vw, 3.2rem); max-width: 17ch; }
.band.sheet { background: var(--sheet); }
.band.pine { background: var(--pine); color: var(--sheet); }

/* Keyed on .on-pine, which the masthead, the hero, both pine bands and the
   footer all carry — NOT on .band.pine, which the hero is not. The first
   version styled only the bands, so the hero's eyebrow fell through to its
   default --ink-3 and painted 2.19:1 on pine. The check:contrast script passed
   it, because that script measures the pairs it is TOLD about and
   ink-3-on-pine is a pair nobody intended to paint; the in-page sweep in the
   browser suite is what caught it. That is the argument for having both.

   Listed element by element rather than as one rule on every p, because the hero
   holds white UI fragments inside a pine band: a blanket rule would repaint
   their text pine-tint on white, which is the same bug pointing the other
   way. */
.on-pine h1, .on-pine h2, .on-pine h3 { color: var(--sheet); }
.on-pine .eyebrow { color: var(--pine-tint); }
.on-pine .lede { color: var(--pine-tint); }
.band.pine > .wrap > p { color: var(--pine-tint); }
.band.pine .check p { color: var(--pine-tint); }

/* No blanket weight. With no photograph the headline IS the image, and an
   image cannot be at the same weight as the labels around it — light display
   against heavy micro is the contrast that reads as art direction rather than
   as defaults. The face is a real variable 300-700 master, so 300 is drawn and
   not a synthesised thin. */
h1, h2, h3 { letter-spacing: -0.03em; margin: 0; text-wrap: balance; }
/* pretty on running text rather than balance: balance evens a heading's lines,
   and on a paragraph it only moves the orphan somewhere else. */
p, li { text-wrap: pretty; }
/* The default h1. Neither hero uses it as is any more: the homepage's layered
   hero sets 3.9rem so its headline holds three lines beside the scene, and the
   studio pages set 3.6rem over 16ch. It stays for any page that has neither. */
h1 { font-size: clamp(2.6rem, 6vw, 4.4rem); font-weight: 300; letter-spacing: -0.035em; line-height: 0.99; }
h2 { font-size: clamp(1.9rem, 3.8vw, 2.7rem); font-weight: 400; line-height: 1.06; max-width: 20ch; }
h3 { font-size: 1.1rem; font-weight: 600; letter-spacing: -0.008em; }
p { margin: 0 0 var(--s4); max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

.eyebrow {
  /* 600 with wider tracking, against an h1 at 300. That inversion — light
     display, heavy micro — is the whole type idea, and at 700 the eyebrow
     competes with the headline instead of labelling it. */
  font-size: 13px; font-weight: 600; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--ink-3); margin: 0 0 var(--s3);
}
.lede { font-size: 1.18rem; color: var(--ink-2); max-width: 40rem; }
.sub { color: var(--ink-2); margin-top: var(--s3); }

/* ── three button labels, and there are only three ─────────────────────── */
.btn {
  display: inline-block; padding: 15px var(--s6); border-radius: 999px;
  font-weight: 600; font-size: 16px; border: 2px solid transparent; cursor: pointer;
  background: var(--pine); color: var(--sheet);
}
.btn:hover { background: var(--pine-deep); color: var(--sheet); }
/* Eased rather than switched, and only where BOTH ends of the ease clear AA
   against each other at every step: pine to pine-deep under white, and white
   to pine-tint under pine, are two grounds of the same lightness band. .line
   is excluded on purpose: it swaps ink and ground at once, so halfway through
   the label is ink on half-ink, and a sweep at rest can never see that. */
.topnav a, .foot-grid a { transition: color 200ms ease; }
/* A button that moves on hover and not on press is a picture of a button.
   120ms, because a press has to feel instant to feel physical. */
.faq summary:active { transform: translateY(1px); }
.btn.light { background: var(--sheet); color: var(--pine); }
.btn.light:hover { background: var(--pine-tint); color: var(--pine-deep); }
.btn.line { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn.line:hover { background: var(--ink); color: var(--paper); }
.on-pine .btn.line { color: var(--sheet); border-color: var(--pine-tint); }
.on-pine .btn.line:hover { background: var(--sheet); color: var(--pine); }
.actions { display: flex; gap: var(--s3); flex-wrap: wrap; align-items: center; margin-top: var(--s7); }

/* ── texture and lit grounds ───────────────────────────────────────────── */
/*
   One ::before, two fills. NOT background-image on the band itself:
   .band.sheet { background: var(--sheet) } is a SHORTHAND and resets
   background-image — the same specificity trap that once cost four sections
   224px of padding. A pseudo-element owns its own box and cannot collide.

   No url() anywhere, which matters beyond the CSP: externaliseStyles() lifts
   the stylesheet into styles.css BEFORE assetsReferencedBy scans index.html,
   so a url() in CSS is invisible to the asset gate and would 404 in silence on
   the static host.
*/
.tex, .lit { position: relative; }
.tex > .wrap, .lit > .wrap { position: relative; z-index: 1; }
.tex::before, .lit::before { content: ''; position: absolute; inset: 0; pointer-events: none; }

/* Graph paper at 26px — the drafting grid a floor plan is set out on. The dot
   is feathered rather than hard-stopped: an aliased 1px dot shimmers on a
   fractional device pixel ratio. */
.tex::before {
  background-image: radial-gradient(circle at 1px 1px, var(--tex) 1.1px, rgba(230, 234, 231, 0) 1.6px);
  background-size: 26px 26px;
  background-position: 13px 13px;
}

/* Two stops of the SAME hue, lit top-right and sunk bottom-left. Depth rather
   than a second colour — which is what makes a 900px slab of one hex stop
   reading as a paint bucket. */
.lit::before {
  background-image:
    radial-gradient(118% 88% at 78% 6%, var(--pine-lit), rgba(32, 73, 62, 0) 56%),
    radial-gradient(92% 72% at 4% 104%, var(--pine-deep), rgba(10, 42, 34, 0) 60%);
}

/* Three classes, so this beats .band.pine without !important. */
.band.pine.deep { background: var(--pine-deep); }
footer { background: var(--pine-deep); }

/* ── the announcement strip ────────────────────────────────────────────── */
/* The most expensive promise on the page, quarantined in one line so the
   headline never has to carry it. Its ink/ground pair is declared in
   check:contrast rather than left to the in-page sweep to discover. */
.announce { background: var(--pine-tint); color: var(--pine-deep); font-size: 14px; }
.announce a { display: block; text-align: center; padding: var(--s3) var(--s5); }
.announce a:hover { text-decoration: underline; }

/* ── masthead ──────────────────────────────────────────────────────────── */
.top { background: var(--pine); color: var(--sheet); }
.top .wrap { display: flex; align-items: center; gap: var(--s5); padding-top: var(--s4); padding-bottom: var(--s4); flex-wrap: wrap; }
.mark { font-weight: 700; font-size: 19px; letter-spacing: -0.02em; white-space: nowrap; }
.topnav { display: flex; gap: var(--s5); margin-left: auto; font-size: 15px; flex-wrap: wrap; align-items: center; }
.topnav a { color: var(--pine-tint); }
.topnav a:hover { color: var(--sheet); }
.topnav .btn { padding: 9px var(--s5); font-size: 15px; }

/* ── hero ──────────────────────────────────────────────────────────────── */
.hero { background: var(--pine); color: var(--sheet); padding: var(--s9) 0 var(--s9); }
.hero h1 { color: var(--sheet); max-width: 15ch; }
.hero .lede { color: var(--pine-tint); margin-top: var(--s5); }
.hero-grid { display: grid; grid-template-columns: 7fr 5fr; gap: var(--s8); align-items: start; }
.hero-note { margin-top: var(--s4); font-size: 14px; color: var(--pine-tint); }

/* ── live UI fragments: the interface, not a picture of it ─────────────── */
.frag {
  background: var(--sheet); color: var(--ink);
  border-radius: 16px; padding: var(--s4) var(--s5);
  box-shadow: 0 2px 4px rgba(10, 42, 34, 0.12), 0 18px 40px rgba(10, 42, 34, 0.26);
}
.frag-h { font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 var(--s3); }

.seats { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; }
.seat {
  aspect-ratio: 3 / 4; border-radius: 8px; border: 2px solid var(--rule);
  background: var(--sheet); display: flex; flex-direction: column; gap: 2px; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600; color: var(--ink-2);
}
.seat .glyph { width: 74%; height: auto; overflow: visible; }
.seat .bike { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
/* The page colour, never a state colour: invisible on a free seat's sheet,
   a rider on a taken seat's pine. See BIKE in proof.ts. */
.seat .rider { fill: var(--sheet); stroke: var(--sheet); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.seat.taken { background: var(--pine); border-color: var(--pine); color: var(--sheet); }
/* Yours and taken are opposite facts about the same square of floor, so they
   are drawn as opposites rather than as two shades of the same grey. */
.seat.mine { background: var(--sheet); border-color: var(--ink); color: var(--ink); box-shadow: inset 0 0 0 3px var(--ink); }
/* Solid and unnamed. The product's own rule: a named dark cell is a seat, an
   unnamed one is the room. Drawn in the sunk grey it read as a FREE seat, which is the
   opposite of what a pillar is — and it is the one cell the key beside the grid
   makes a claim about. */
.seat.wall { background: var(--ink-3); border-color: var(--ink-3); }
.seat.fills { animation: fill 7s steps(1, end) infinite; }
.seat.fills2 { animation: fill 7s steps(1, end) infinite 2.4s; }
@keyframes fill {
  0%, 38% { background: var(--sheet); border-color: var(--rule); color: var(--ink-2); }
  40%, 100% { background: var(--pine); border-color: var(--pine); color: var(--sheet); }
}
.seat-key { margin: var(--s3) 0 0; font-size: 12px; color: var(--ink-2); display: flex; gap: var(--s4); flex-wrap: wrap; }


/* ── the full screenshot: legible, not a device mockup ─────────────────── */
figure { margin: 0; }
/* The one overlap on the page, and the only object that occupies two grounds:
   the white sheet of the screenshot cuts up into the pine-deep band above it,
   which is what makes the eye read it as the subject rather than as decoration
   sitting in its own box. A negative margin rather than a transform, so the
   band still reserves the space and nothing collides underneath. */
.figure-gap { margin: 0 0 var(--s8); }
.overhang { padding-top: 0; }
/* -200px, not -104: the band above carries .heavy (160px of bottom padding),
   so anything less than that lands the image BELOW the seam and the overlap
   reads as a gap rather than as layering. 200 puts its top 40px into the dark,
   which clears that band's last element. */
.overhang .figure-gap { margin-top: -200px; position: relative; z-index: 2; }
.sub.tight { font-size: 15px; }
.shot {
  width: 100%; height: auto; display: block;
  border-radius: 16px; border: 1px solid var(--rule);
  box-shadow: 0 3px 6px rgba(18, 32, 27, 0.07), 0 36px 72px rgba(18, 32, 27, 0.18);
}
figcaption { margin-top: var(--s3); font-size: 14px; color: var(--ink-2); max-width: var(--measure); }

/* ── the three jobs ────────────────────────────────────────────────────── */
.trio { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s6); margin-top: var(--s8); }

/* Four things that happen in ORDER every day are a sequence, not a set — so
   they stop being four equal cards and become a numbered ledger. Three
   consecutive equal-column grids is the single most generic layout a page can
   have, and this was the first of them. */
.trio.four { grid-template-columns: 1fr; gap: 0; margin-top: var(--s7); counter-reset: job; }
.trio.four .third {
  counter-increment: job;
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 13ch) minmax(0, 34rem);
  align-items: baseline;
  gap: var(--s3) var(--s6);
  border-top: 1px solid var(--rule);
  padding: var(--s5) 0;
}
.trio.four .third:last-child { border-bottom: 1px solid var(--rule); }
.trio.four .third::before {
  content: '0' counter(job);
  font-size: 2.2rem;
  font-weight: 300;
  line-height: 0.85;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}
.trio.four h3 { font-size: 1.15rem; margin: 0; }
.trio.four p { margin: 0; color: var(--ink-2); font-size: 16px; }
.third { border-top: 2px solid var(--ink); padding-top: var(--s4); }
.third h3 { margin-bottom: var(--s3); }
.third p { color: var(--ink-2); font-size: 15.5px; margin: 0; }

/* ── the three rooms ───────────────────────────────────────────────────── */
/* The second of the three equal-column grids, and the one where equality was
   costing the most: a floor plan is the only image on this page a buyer cannot
   accuse of being borrowed — a photograph is always somebody else's studio,
   a plan is a diagram of theirs — and all three were drawn at icon size while
   "drawn to scale" carried the band. One is promoted, two support it. */
.rooms {
  display: grid;
  grid-template-columns: 1.55fr 1fr;
  gap: var(--s7) var(--s8);
  margin-top: var(--s8);
  align-items: start;
}
.rooms .room:first-child { grid-row: 1 / span 2; }
.rooms .room:first-child h3 { font-size: 1.7rem; font-weight: 400; letter-spacing: -0.025em; }
.rooms .room:first-child p { font-size: 1.05rem; max-width: 30rem; }
.room { border-top: 2px solid var(--ink); padding-top: var(--s4); }
.room svg {
  display: block; width: 100%; height: auto;
  margin-bottom: var(--s4); border-radius: 10px;
  background: var(--sheet);
  /* A shadow, NOT a border. The in-page sweep measures a painted boundary
     against its ground and holds it to the 3:1 non-text floor: --rule on
     --sheet is about 1.6:1, so a 1px frame here fails at every viewport. The
     white ground against the lattice is already the edge; the shadow only
     lifts it off. */
  box-shadow: 0 1px 2px rgba(18, 32, 27, 0.05), 0 8px 24px rgba(18, 32, 27, 0.06);
}
/* Load-bearing, not polish. Without it a 1.25px hairline thickens to roughly
   4px when the viewBox scales to 700px, and the drawing collapses back into
   the chiclets it was promoted out of. */
.room svg rect, .room svg line, .room svg path { vector-effect: non-scaling-stroke; }
.room p { margin: var(--s3) 0 0; color: var(--ink-2); font-size: 16px; }



/* ── pricing ───────────────────────────────────────────────────────────── */
.price-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s8); margin-top: var(--s7); align-items: start; }
.per { color: var(--ink-2); margin-top: var(--s3); }
.price-list { margin: 0; padding: 0; list-style: none; }
.price-list li { padding: var(--s3) 0; border-top: 1px solid var(--rule); display: flex; gap: var(--s4); font-size: 15.5px; }
.price-list li:last-child { border-bottom: 1px solid var(--rule); }
.price-list .k { flex: 1; color: var(--ink-2); }
.price-list .v { font-weight: 600; text-align: right; }

/* ── checkable claims ──────────────────────────────────────────────────── */
.checks { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s5) var(--s6); margin-top: var(--s7); }
/* The third equal-column grid, and the only one whose content genuinely IS
   three of a kind — so it keeps the grid and loses the row. Three margins is
   the cheapest asymmetry available and carries no layout risk. */
.band.heavy .checks .check:nth-child(2) { margin-top: var(--s5); }
.band.heavy .checks .check:nth-child(3) { margin-top: var(--s7); }
.check { border-top: 1px solid rgba(223, 233, 227, 0.4); padding-top: var(--s4); }
/* The honest list sits on paper rather than on pine, so the rgba rule above —
   tuned for the dark ground — would be invisible under it. */
#not-built { border-top: 0; margin-top: var(--s6); max-width: 38rem; }
#not-built .price-list .v { color: var(--ink-2); font-weight: 500; }
.check h3 { font-size: 1rem; margin-bottom: var(--s2); }
.check p { font-size: 15px; margin: 0; max-width: none; }

/* ── faq ───────────────────────────────────────────────────────────────── */
/* Four short answers behind three clicks, on a page whose whole argument is
   that it answers plainly. An accordion is the audit's named cliche and it was
   also hiding the only risk-reversal on the page. Open ledger instead. */
.faq { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s7) var(--s8); margin-top: var(--s8); }
.faq .qa { border-top: 2px solid var(--ink); padding-top: var(--s4); }
.faq .qa h3 { font-size: 1.05rem; font-weight: 600; margin: 0 0 var(--s3); }
.faq .qa p { margin: 0; color: var(--ink-2); font-size: 15.5px; max-width: 32rem; }

/* ── illustrations ─────────────────────────────────────────────────────── */
/* Two drawings, both on LIGHT bands, each beside a heading rather than inside
   a card. On pine every dark in the source becomes the ground and the figure
   turns into a silhouette, which was rendered and rejected; a floor plan is
   its own band, never shared with a drawing. The
   heading keeps its column and the drawing takes what was empty space to the
   right of it, so no copy moved. */
.headrow { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; column-gap: var(--s6); }
.headrow > div { padding-bottom: var(--s7); }
/* Zeroed so the bike's floor line lands ON the ledger's first rule. That
   alignment is the reason the drawing reads as sitting in the band rather than
   pasted beside it. */
.headrow + .trio.four, .headrow + .faq { margin-top: 0; }
.ill { display: block; height: auto; }
.ill-bike { width: 300px; }
.ill-calm { width: 250px; margin-bottom: var(--s5); }
/* Every drawing beside a heading gets a width from HERE, not one drawing at a
   time: an svg with height auto and no width in a grid track sized auto
   collapses to nothing. Patching each drawing as it arrived is how it shipped
   invisible three times — the first ledger drawing, then five FAQ drawings at
   once. The browser suite now fails any drawing narrower than 60px. */
.headrow > .ill { width: 280px; }

/* Motion inside the drawings. The owner, after the first version: "I see
   very little or no animations within the svgs". So each drawing now moves
   the parts that can move without coming apart — limbs from the joint a
   sleeve or a hem covers, heads from a neck base tucked under a collar,
   props, plants, clouds — each proven in rendered frames at both ends of its
   range before it was kept, and each drawing's list of parts that could NOT
   be moved is in its recipe.

   The rules, every one of them paid for:
   - Transform only, so nothing here touches layout or paint.
   - Everything is SCOPED to its drawing (.ill-bike .ill-nod, not .ill-nod):
     the drawings were worked out independently, the same short names mean
     different motions in different drawings, and an unscoped rule animates
     every drawing that happens to share a class.
   - A hooked group carries no transform attribute of its own, because a CSS
     transform REPLACES one rather than composing with it.
   - A pivot is a fill-box percentage when the part's own box locates it, and
     a view-box pixel (transform-box: view-box) when a joint lies outside the
     part — a hand that must move with an arm it is not a neighbour of gets
     the arm's exact pivot and animation, and so moves as one rigid piece.
   - Durations differ, so no two motions stay in step and nothing reads as a
     metronome. */
.ill-spin { transform-box: fill-box; transform-origin: 50% 50%; animation: ill-spin 4s linear infinite; }
.ill-sway { transform-box: fill-box; transform-origin: 50% 100%; animation: ill-sway 6s ease-in-out infinite alternate; }
.ill-late { animation-delay: -2.5s; }
@keyframes ill-spin { to { transform: rotate(360deg); } }
@keyframes ill-sway { from { transform: rotate(-1.6deg); } to { transform: rotate(1.6deg); } }

/* bike */
.ill-bike .ill-spin { animation-duration: 1.8s; }
.ill-bike .ill-sway { animation: bike-sway 3.6s ease-in-out infinite alternate; }
.ill-bike .ill-lean { transform-box: fill-box; transform-origin: 50% 100%; animation: bike-lean 4.4s ease-in-out infinite alternate; }
.ill-bike .ill-chain { animation: bike-chain 0.9s linear infinite; }
.ill-bike .ill-chain-low { animation: bike-chain-low 0.9s linear infinite; }
.ill-bike .ill-nod { transform-box: view-box; transform-origin: 497px 192px; animation: bike-nod 0.9s ease-in-out infinite alternate; }
.ill-bike .ill-pulse { transform-box: fill-box; transform-origin: 50% 50%; animation: bike-pulse 1.1s ease-in-out infinite; }
@keyframes bike-sway { from { transform: rotate(-5deg); } to { transform: rotate(5deg); } }
@keyframes bike-lean { from { transform: rotate(-7deg); } to { transform: rotate(0.5deg); } }
@keyframes bike-chain { to { transform: translateX(19.1px); } }
@keyframes bike-chain-low { to { transform: translate(-19.08px, 1.48px); } }
@keyframes bike-nod { from { transform: rotate(-2deg); } to { transform: rotate(3.5deg); } }
@keyframes bike-pulse { 0%, 100% { transform: scale(1); } 15% { transform: scale(1.35); } 30% { transform: scale(1); } 45% { transform: scale(1.2); } 60% { transform: scale(1); } }

/* calm */
.ill-calm .ill-tilt { transform-box: fill-box; transform-origin: 49% 98%; animation: calm-tilt 5.2s ease-in-out infinite alternate; }
.ill-calm .ill-sway { animation: calm-sway 3.8s ease-in-out infinite alternate; }
.ill-calm .ill-late { animation-duration: 4.9s; animation-delay: -2.2s; }
.ill-calm .ill-glint { transform-box: fill-box; transform-origin: 50% 50%; animation: calm-glint 2.6s ease-in-out infinite alternate; }
.ill-calm .ill-glint-b { animation-duration: 3.3s; animation-delay: -1.2s; }
.ill-calm .ill-glint-c { animation-duration: 2.1s; animation-delay: -0.6s; }
@keyframes calm-tilt { from { transform: rotate(-4.5deg); } to { transform: rotate(4.5deg); } }
@keyframes calm-sway { from { transform: rotate(-7deg); } to { transform: rotate(7deg); } }
@keyframes calm-glint { from { transform: scale(0.35) rotate(0deg); } to { transform: scale(1.1) rotate(90deg); } }

/* rings */
.ill-rings .ill-step, .ill-rings .ill-point, .ill-rings .ill-nod, .ill-rings .ill-arc { transform-box: view-box; }
.ill-rings .ill-point { transform-origin: 790px 458px; animation: rings-point 2.6s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-rings .ill-nod { transform-origin: 820px 436px; animation: rings-nod 3.4s ease-in-out infinite alternate; }
.ill-rings .ill-step { transform-origin: 790px 620px; animation: rings-step 1.9s ease-in-out infinite alternate; }
.ill-rings .ill-arc { transform-origin: 284px 168.3px; animation: rings-arc 5.2s ease-in-out infinite alternate; }
.ill-rings .ill-arc-b { transform-origin: 100.5px 460.7px; animation-duration: 4.3s; animation-delay: -1.5s; }
.ill-rings .ill-arc-c { transform-origin: 467.5px 460.7px; animation-duration: 6.1s; animation-delay: -3s; }
.ill-rings .ill-lift { transform-box: fill-box; transform-origin: 50% 50%; animation: rings-lift 2.2s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-rings .ill-bob { transform-box: fill-box; animation: rings-bob 3.2s ease-in-out infinite alternate; }
.ill-rings .ill-beat { transform-box: fill-box; transform-origin: 50% 50%; animation: rings-beat 1.4s ease-in-out infinite; }
@keyframes rings-point { from { transform: rotate(-8deg); } to { transform: rotate(9deg); } }
@keyframes rings-nod { from { transform: rotate(-4deg); } to { transform: rotate(5deg); } }
@keyframes rings-step { from { transform: rotate(-5deg); } to { transform: rotate(7deg); } }
@keyframes rings-arc { from { transform: rotate(-22deg); } to { transform: rotate(22deg); } }
@keyframes rings-lift { from { transform: translateY(8px) rotate(6deg); } to { transform: translateY(-14px) rotate(-6deg); } }
@keyframes rings-bob { from { transform: translateY(0); } to { transform: translateY(-14px); } }
@keyframes rings-beat { 0%, 100% { transform: scale(1); } 14% { transform: scale(1.22); } 28% { transform: scale(0.96); } 40% { transform: scale(1); } }

/* phone */
.ill-phone .ill-hill, .ill-phone .ill-ping, .ill-phone .ill-drift, .ill-phone .ill-kick, .ill-phone .ill-tail, .ill-phone .ill-arm-r, .ill-phone .ill-arm-l { transform-box: fill-box; }
.ill-phone .ill-hill { transform-origin: 50% 100%; animation: phone-hill 3.8s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-phone .ill-hill-b { animation-duration: 2.9s; animation-delay: -1.4s; }
.ill-phone .ill-ping { transform-origin: 50% 50%; animation: phone-ping 2.4s ease-in-out infinite; }
.ill-phone .ill-ping-b { animation-delay: -1.6s; }
.ill-phone .ill-ping-c { animation-delay: -0.8s; }
.ill-phone .ill-drift { animation: phone-drift 7s ease-in-out infinite alternate; }
.ill-phone .ill-drift-r { animation-name: phone-drift-r; animation-duration: 8.5s; animation-delay: -3s; }
.ill-phone .ill-drift-l { animation-name: phone-drift-l; animation-duration: 6s; animation-delay: -1s; }
.ill-phone .ill-kick { transform-origin: 75% 2%; animation: phone-kick 1.3s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-phone .ill-tail { transform-origin: 90% 34%; animation: phone-tail 1.3s ease-in-out -0.3s infinite alternate; }
.ill-phone .ill-arm-r { transform-origin: 19% 5%; animation: phone-arm 1.3s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-phone .ill-arm-l { transform-origin: 80% 4%; animation: phone-arm 1.3s cubic-bezier(.45, 0, .25, 1) infinite alternate-reverse; }
@keyframes phone-hill { from { transform: scaleY(0.84); } to { transform: scaleY(1.05); } }
@keyframes phone-ping { 0%, 60%, 100% { transform: scale(1); } 75% { transform: scale(1.6); } }
@keyframes phone-drift { from { transform: translateX(-28px); } to { transform: translateX(28px); } }
@keyframes phone-drift-r { from { transform: translateX(0); } to { transform: translateX(-40px); } }
@keyframes phone-drift-l { from { transform: translateX(0); } to { transform: translateX(40px); } }
@keyframes phone-kick { from { transform: rotate(-10deg); } to { transform: rotate(1deg); } }
@keyframes phone-tail { from { transform: rotate(-9deg); } to { transform: rotate(7deg); } }
@keyframes phone-arm { from { transform: rotate(-8deg); } to { transform: rotate(6deg); } }

/* ball */
.ill-ball .ill-hair, .ill-ball .ill-kick, .ill-ball .ill-kick2, .ill-ball .ill-cloud-a, .ill-ball .ill-cloud-b, .ill-ball .ill-cloud-c, .ill-ball .ill-bush { transform-box: fill-box; }
.ill-ball .ill-hair { transform-origin: 40% 4%; animation: ball-hair 2.6s ease-in-out infinite alternate; }
.ill-ball .ill-kick { transform-origin: 5% 12%; animation: ball-kick 3.4s ease-in-out infinite alternate; }
.ill-ball .ill-kick2 { transform-origin: 5% 12%; animation: ball-kick2 3.4s ease-in-out infinite alternate; }
.ill-ball .ill-cloud-a { animation: ball-cloud 9s ease-in-out infinite alternate; }
.ill-ball .ill-cloud-b { animation: ball-cloud 6.5s ease-in-out infinite alternate-reverse; }
.ill-ball .ill-cloud-c { animation: ball-cloud 11s ease-in-out infinite alternate; }
.ill-ball .ill-bush { transform-origin: 50% 100%; animation: ball-bush 4.2s ease-in-out infinite alternate; }
@keyframes ball-hair { from { transform: rotate(-7deg); } to { transform: rotate(6deg); } }
@keyframes ball-kick { from { transform: rotate(0deg); } to { transform: rotate(-9deg); } }
@keyframes ball-kick2 { from { transform: rotate(-8deg); } to { transform: rotate(0deg); } }
@keyframes ball-cloud { from { transform: translateX(-28px); } to { transform: translateX(28px); } }
@keyframes ball-bush { from { transform: rotate(-3deg); } to { transform: rotate(3deg); } }

/* lunge: the dip scales the body down from the floor line at y=560; the
   ponytail's keyframes compose that same dip with its own swing, because a
   hook may not nest inside another and a separate loop would pull it off the
   head. */
.ill-lunge .ill-leaf { transform-box: fill-box; animation: lunge-leaf 3s ease-in-out infinite alternate; }
.ill-lunge .ill-dip, .ill-lunge .ill-dip2, .ill-lunge .ill-tail { transform-box: view-box; transform-origin: 0 0; }
.ill-lunge .ill-dip, .ill-lunge .ill-dip2 { animation: lunge-dip 3.2s ease-in-out infinite alternate; }
.ill-lunge .ill-tail { animation: lunge-tail 3.2s ease-in-out infinite alternate; }
.ill-lunge .ill-leaf-1 { transform-origin: 95% 95%; animation-duration: 3.1s; }
.ill-lunge .ill-leaf-2 { transform-origin: 5% 95%; animation-duration: 2.7s; animation-direction: alternate-reverse; }
.ill-lunge .ill-leaf-3 { transform-origin: 98% 85%; animation-duration: 3.5s; animation-direction: alternate-reverse; }
.ill-lunge .ill-leaf-4 { transform-origin: 2% 90%; animation-duration: 2.9s; }
.ill-lunge .ill-leaf-5 { transform-origin: 98% 30%; animation-duration: 3.3s; }
.ill-lunge .ill-leaf-6 { transform-origin: 2% 35%; animation-duration: 2.5s; animation-direction: alternate-reverse; }
@keyframes lunge-dip { from { transform: translate(0px, 560px) scaleY(1) translate(0px, -560px); } to { transform: translate(0px, 560px) scaleY(0.97) translate(0px, -560px); } }
@keyframes lunge-tail {
  from { transform: translate(0px, 560px) scaleY(1) translate(0px, -560px) translate(301px, 10px) rotate(1deg) translate(-301px, -10px); }
  to { transform: translate(0px, 560px) scaleY(0.97) translate(0px, -560px) translate(301px, 10px) rotate(-10deg) translate(-301px, -10px); }
}
@keyframes lunge-leaf { from { transform: rotate(-7deg); } to { transform: rotate(7deg); } }

/* tree */
.ill-tree .ill-stem, .ill-tree .ill-leaf-r, .ill-tree .ill-leaf-l, .ill-tree .ill-bush, .ill-tree .ill-balance, .ill-tree .ill-spark { transform-box: fill-box; }
.ill-tree .ill-stem { transform-origin: 35.9% 99.8%; animation: tree-stem 3.4s ease-in-out infinite alternate; }
.ill-tree .ill-leaf-r { transform-origin: 5.8% 100%; animation: tree-leaf-r 2.3s ease-in-out infinite alternate; }
.ill-tree .ill-leaf-l { transform-origin: 99.8% 98.3%; animation: tree-leaf-l 2.9s ease-in-out infinite alternate; }
.ill-tree .ill-bush { transform-origin: 64.3% 99.7%; animation: tree-bush 4.1s ease-in-out infinite alternate; }
.ill-tree .ill-balance { transform-origin: 64.4% 96.9%; animation: tree-balance 5.2s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-tree .ill-spark { transform-origin: 50% 50%; animation: tree-spark 1.9s ease-in-out infinite alternate; }
.ill-tree .ill-spark-late { animation-delay: -0.95s; }
@keyframes tree-stem { from { transform: rotate(-6deg); } to { transform: rotate(5deg); } }
@keyframes tree-leaf-r { from { transform: rotate(-4deg); } to { transform: rotate(12deg); } }
@keyframes tree-leaf-l { from { transform: rotate(-12deg); } to { transform: rotate(4deg); } }
@keyframes tree-bush { from { transform: skewX(-9deg); } to { transform: skewX(7deg); } }
@keyframes tree-balance { from { transform: rotate(-1.6deg); } to { transform: rotate(1.6deg); } }
@keyframes tree-spark { from { transform: scale(0.4) rotate(0deg); } to { transform: scale(1.35) rotate(90deg); } }

/* ledger: legs and shoes are separate shapes that share one pivot and one
   loop, so each shoe stays on its leg; one stride is 1.8s. */
.ill-ledger .ill-tree-a, .ill-ledger .ill-tree-b, .ill-ledger .ill-leg-f, .ill-ledger .ill-shoe-f, .ill-ledger .ill-leg-b, .ill-ledger .ill-shoe-b, .ill-ledger .ill-hand-f, .ill-ledger .ill-hand-b, .ill-ledger .ill-head { transform-box: fill-box; }
.ill-ledger .ill-tree-a { transform-origin: 48.2% 100%; animation: ledger-tree 3.7s ease-in-out infinite alternate; }
.ill-ledger .ill-tree-b { transform-origin: 48.4% 99.3%; animation: ledger-tree 4.6s ease-in-out -1.3s infinite alternate; }
.ill-ledger .ill-leg-f { transform-origin: 78.9% 9.5%; animation: ledger-leg-f 0.9s ease-in-out infinite alternate; }
.ill-ledger .ill-shoe-f { transform-origin: 53.3% -181.2%; animation: ledger-leg-f 0.9s ease-in-out infinite alternate; }
.ill-ledger .ill-leg-b { transform-origin: 7.9% 4.4%; animation: ledger-leg-b 0.9s ease-in-out infinite alternate; }
.ill-ledger .ill-shoe-b { transform-origin: -277.5% -122%; animation: ledger-leg-b 0.9s ease-in-out infinite alternate; }
.ill-ledger .ill-hand-f { transform-origin: 72.9% 93.2%; animation: ledger-hand-f 0.9s ease-in-out infinite alternate; }
.ill-ledger .ill-hand-b { transform-origin: 51.9% 7.1%; animation: ledger-hand-b 0.9s ease-in-out infinite alternate; }
.ill-ledger .ill-head { transform-origin: 75.7% 81.5%; animation: ledger-head 0.9s ease-in-out infinite alternate; }
@keyframes ledger-tree { from { transform: rotate(-3deg); } to { transform: rotate(3deg); } }
@keyframes ledger-leg-f { from { transform: rotate(-6deg); } to { transform: rotate(7deg); } }
@keyframes ledger-leg-b { from { transform: rotate(0deg); } to { transform: rotate(-9deg); } }
@keyframes ledger-hand-f { from { transform: rotate(8deg); } to { transform: rotate(-8deg); } }
@keyframes ledger-hand-b { from { transform: rotate(-10deg); } to { transform: rotate(12deg); } }
@keyframes ledger-head { from { transform: rotate(-2deg); } to { transform: rotate(2.5deg); } }

/* lotus: overflow visible because her bun touches the top of the view box and
   the float would otherwise clip it. */
.ill-lotus { overflow: visible; }
.ill-lotus .ill-rise, .ill-lotus .ill-heart, .ill-lotus .ill-glint, .ill-lotus .ill-flame { transform-box: fill-box; }
.ill-lotus .ill-rise { transform-origin: 50% 100%; animation: lotus-rise 4.4s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-lotus .ill-heart { transform-origin: 50% 100%; animation: lotus-heart 3.4s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-lotus .ill-glint { transform-origin: 50% 50%; animation: lotus-glint 2.2s ease-in-out infinite alternate; }
.ill-lotus .ill-late { animation-delay: -1.3s; animation-duration: 2.9s; }
.ill-lotus .ill-flame { transform-origin: 50% 100%; animation: lotus-flame 1.3s ease-in-out infinite; }
@keyframes lotus-rise { from { transform: translateY(0) scale(1); } to { transform: translateY(-22px) scale(1.025); } }
@keyframes lotus-heart { from { transform: translateY(6px) scale(0.88); } to { transform: translateY(-34px) scale(1.12); } }
@keyframes lotus-glint { from { transform: scale(0.35) rotate(0deg); } to { transform: scale(1.3) rotate(90deg); } }
@keyframes lotus-flame { 0%, 100% { transform: scale(1, 1) rotate(0deg); } 25% { transform: scale(0.85, 1.3) rotate(-7deg); } 55% { transform: scale(1.1, 0.9) rotate(4deg); } 80% { transform: scale(0.9, 1.2) rotate(6deg); } }

/* start */
.ill-start { overflow: visible; }
.ill-start .ill-set { transform-box: fill-box; transform-origin: 50% 100%; animation: start-set 2.6s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-start .ill-head { transform-box: view-box; transform-origin: 797px 658px; animation: start-head 2.6s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
@keyframes start-set { from { transform: scaleY(1); } to { transform: scaleY(1.05); } }
@keyframes start-head { from { transform: translateY(0) rotate(0deg); } to { transform: translateY(-14px) rotate(9deg); } }

/* pair */
.ill-pair .ill-curl, .ill-pair .ill-nod, .ill-pair .ill-tilt { transform-box: fill-box; }
.ill-pair .ill-lift, .ill-pair .ill-raise { transform-box: view-box; }
.ill-pair .ill-curl { transform-origin: 50% 100%; animation: pair-curl 1.8s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-pair .ill-lift { animation: pair-lift 1.8s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-pair .ill-off { animation-delay: -1.8s; }
.ill-pair .ill-nod { transform-origin: 67% 100%; animation: pair-nod 3.4s ease-in-out infinite alternate; }
.ill-pair .ill-raise { transform-origin: 582px 208px; animation: pair-raise 2.6s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-pair .ill-tilt { transform-origin: 43% 100%; animation: pair-tilt 4.2s ease-in-out infinite alternate; }
@keyframes pair-curl { from { transform: scaleY(1); } to { transform: scaleY(0.8); } }
@keyframes pair-lift { from { transform: translateY(0); } to { transform: translateY(20px); } }
@keyframes pair-nod { from { transform: rotate(-5deg); } to { transform: rotate(5deg); } }
@keyframes pair-raise { from { transform: rotate(0deg); } to { transform: rotate(12deg); } }
@keyframes pair-tilt { from { transform: rotate(4deg); } to { transform: rotate(-4deg); } }

/* bell */
.ill-bell .ill-kb, .ill-bell .ill-toss { transform-box: view-box; }
.ill-bell .ill-kb { transform-origin: 322px 128px; animation: bell-kb 2.2s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-bell .ill-toss { transform-origin: 302px 68px; animation: bell-toss 3.8s ease-in-out infinite alternate; }
@keyframes bell-kb { from { transform: rotate(0deg); } to { transform: rotate(7deg); } }
@keyframes bell-toss { from { transform: rotate(-6deg); } to { transform: rotate(6deg); } }

/* ── Storyset (2026-09-26): the same rules, one scoped block each ── */
.ill-crescent .ill-note { transform-box: fill-box; transform-origin: 50% 100%; animation: crescent-note 2.6s ease-in-out infinite alternate; }
.ill-crescent .ill-note-b { animation-duration: 3.1s; animation-delay: -1.3s; }
.ill-crescent .ill-note-c { animation-duration: 2.3s; animation-delay: -0.7s; }
.ill-crescent .ill-note-d { animation-duration: 3.5s; animation-delay: -2.1s; }
.ill-crescent .ill-wave { transform-box: fill-box; transform-origin: 50% 100%; animation: crescent-wave 1.3s ease-in-out infinite alternate; }
.ill-crescent .ill-leaf, .ill-crescent .ill-frond { transform-box: fill-box; animation: crescent-leaf 4.1s ease-in-out infinite alternate; }
.ill-crescent .ill-leaf-a { transform-origin: 0% 30%; animation-duration: 3.7s; }
.ill-crescent .ill-leaf-b { transform-origin: 0% 60%; animation-duration: 4.4s; animation-delay: -1.8s; }
.ill-crescent .ill-leaf-c { transform-origin: 20% 100%; animation-duration: 3.3s; animation-delay: -0.9s; }
.ill-crescent .ill-leaf-d { transform-origin: 100% 20%; animation-duration: 4.7s; animation-delay: -2.4s; }
.ill-crescent .ill-leaf-e { transform-origin: 100% 10%; animation-duration: 5.3s; animation-delay: -3.1s; }
.ill-crescent .ill-frond { transform-box: view-box; transform-origin: 105px 262px; animation-duration: 3.9s; animation-delay: -1.1s; }
.ill-crescent .ill-reach { transform-box: view-box; transform-origin: 345px 258px; animation: crescent-reach 4.6s ease-in-out infinite alternate; }
.ill-crescent .ill-tilt { transform-box: view-box; transform-origin: 350px 248px; animation: crescent-tilt 5.8s ease-in-out infinite alternate; }
.ill-crescent .ill-mane { animation-name: crescent-mane; }
.ill-crescent .ill-tail { animation-name: crescent-tail; }
.ill-crescent .ill-breathe { transform-box: fill-box; transform-origin: 50% 100%; animation: crescent-breathe 3.4s ease-in-out infinite alternate; }
@keyframes crescent-mane { from { transform: rotate(-3deg) translate(-50px, -48px) rotate(-4deg) translate(50px, 48px); } to { transform: rotate(2.5deg) translate(-50px, -48px) rotate(4deg) translate(50px, 48px); } }
@keyframes crescent-tail { from { transform: rotate(-3deg) translate(-42px, -6px) rotate(-6deg) translate(42px, 6px); } to { transform: rotate(2.5deg) translate(-42px, -6px) rotate(6deg) translate(42px, 6px); } }
@keyframes crescent-note { from { transform: translate(0, 3px) rotate(-8deg); } to { transform: translate(2px, -9px) rotate(8deg); } }
@keyframes crescent-wave { from { transform: scale(0.8); } to { transform: scale(1.12); } }
@keyframes crescent-leaf { from { transform: rotate(-5deg); } to { transform: rotate(5deg); } }
@keyframes crescent-reach { from { transform: rotate(3deg); } to { transform: rotate(-3.5deg); } }
@keyframes crescent-tilt { from { transform: rotate(-3deg); } to { transform: rotate(2.5deg); } }
@keyframes crescent-breathe { from { transform: scale(1, 0.99); } to { transform: scale(1, 1.02); } }

.ill-warrior .ill-plant { transform-box: fill-box; transform-origin: 50% 100%; animation: warrior-plant 4.3s ease-in-out infinite alternate; }
.ill-warrior .ill-reeds { transform-box: fill-box; transform-origin: 50% 100%; animation: warrior-reeds 3.1s ease-in-out infinite alternate; }
.ill-warrior .ill-tilt { transform-box: view-box; transform-origin: 219px 207px; animation: warrior-tilt 5.2s ease-in-out infinite alternate; }
.ill-warrior .ill-locs { animation-name: warrior-locs; }
.ill-warrior .ill-chest { transform-box: view-box; transform-origin: 222px 282px; animation: warrior-breathe 3.7s ease-in-out infinite alternate; }
.ill-warrior .ill-arm-l { transform-box: view-box; transform-origin: 201px 226px; animation: warrior-arm-l 2.8s ease-in-out infinite alternate; }
.ill-warrior .ill-arm-r { transform-box: view-box; transform-origin: 243px 226px; animation: warrior-arm-r 3.2s ease-in-out infinite alternate; animation-delay: -0.3s; }
@keyframes warrior-locs { from { transform: rotate(-5deg) translate(7px, -17px) rotate(-6deg) translate(-7px, 17px); } to { transform: rotate(3deg) translate(7px, -17px) rotate(6deg) translate(-7px, 17px); } }
@keyframes warrior-plant { from { transform: rotate(-3deg); } to { transform: rotate(3deg); } }
@keyframes warrior-reeds { from { transform: rotate(-4deg); } to { transform: rotate(4deg); } }
@keyframes warrior-tilt { from { transform: rotate(-5deg); } to { transform: rotate(3deg); } }
@keyframes warrior-breathe { from { transform: scale(1, 0.99); } to { transform: scale(1, 1.025); } }
@keyframes warrior-arm-l { from { transform: rotate(-3deg); } to { transform: rotate(4deg); } }
@keyframes warrior-arm-r { from { transform: rotate(3deg); } to { transform: rotate(-4deg); } }

.ill-dancer .ill-sway { transform-box: fill-box; transform-origin: 40% 100%; animation: dancer-sway 3.4s ease-in-out infinite alternate; }
.ill-dancer .ill-late { transform-origin: 54% 100%; animation-duration: 4.1s; animation-delay: -1.6s; }
.ill-dancer .ill-spin { transform-box: fill-box; transform-origin: 50% 50%; animation: dancer-spin 24s linear infinite; }
.ill-dancer .ill-nod { transform-box: view-box; transform-origin: 287px 224px; animation: dancer-nod 2.9s ease-in-out infinite alternate; }
.ill-dancer .ill-hair { transform-box: view-box; transform-origin: 300px 208px; animation: dancer-hair 2.3s ease-in-out infinite alternate; }
.ill-dancer .ill-breathe { transform-box: fill-box; transform-origin: 50% 100%; animation: dancer-breathe 3.7s ease-in-out infinite alternate; }
@keyframes dancer-sway { from { transform: rotate(-4deg); } to { transform: rotate(4deg); } }
@keyframes dancer-spin { to { transform: rotate(360deg); } }
@keyframes dancer-nod { from { transform: rotate(-3deg); } to { transform: rotate(2.5deg); } }
@keyframes dancer-hair { from { transform: rotate(-5deg); } to { transform: rotate(4deg); } }
@keyframes dancer-breathe { from { transform: scale(1, 1); } to { transform: scale(1.02, 1.04); } }

.ill-homeflow .ill-swing { transform-box: view-box; transform-origin: 444.5px 24px; animation: homeflow-swing 3.6s ease-in-out infinite alternate; }
.ill-homeflow .ill-pulse { transform-box: fill-box; transform-origin: 50% 100%; animation: homeflow-pulse 0.9s ease-in-out infinite alternate; }
.ill-homeflow .ill-note { transform-box: fill-box; transform-origin: 50% 50%; animation: homeflow-note 2.2s ease-in-out infinite alternate; }
.ill-homeflow .ill-note-b { animation-duration: 1.7s; animation-delay: -0.8s; }
.ill-homeflow .ill-note-c { animation-duration: 2.6s; animation-delay: -1.9s; }
.ill-homeflow .ill-tail { transform-box: fill-box; transform-origin: 82% 25%; animation: homeflow-tail 1.9s ease-in-out infinite alternate; }
.ill-homeflow .ill-ear { transform-box: fill-box; transform-origin: 50% 100%; animation: homeflow-ear 3.1s ease-in-out infinite alternate; }
.ill-homeflow .ill-ear-b { animation-duration: 2.7s; animation-delay: -1.3s; }
.ill-homeflow .ill-breathe { transform-box: view-box; transform-origin: 250px 221px; animation: homeflow-breathe 4.3s ease-in-out infinite alternate; }
.ill-homeflow .ill-nod { transform-box: view-box; transform-origin: 245px 124px; animation: homeflow-nod 3.3s ease-in-out infinite alternate; }
@keyframes homeflow-swing { from { transform: rotate(-2.5deg); } to { transform: rotate(2.5deg); } }
@keyframes homeflow-pulse { from { transform: scale(1, 1); } to { transform: scale(1.04, 1.06); } }
@keyframes homeflow-note { from { transform: translate(0, 4px) rotate(-10deg); } to { transform: translate(3px, -8px) rotate(10deg); } }
@keyframes homeflow-tail { from { transform: rotate(-10deg); } to { transform: rotate(8deg); } }
@keyframes homeflow-ear { from { transform: rotate(-8deg); } to { transform: rotate(6deg); } }
@keyframes homeflow-breathe { from { transform: scale(1, 1); } to { transform: scale(1.02, 1.035); } }
@keyframes homeflow-nod { from { transform: rotate(-4deg); } to { transform: rotate(3deg); } }

.ill-still .ill-leg, .ill-still .ill-arm-r, .ill-still .ill-arm-l, .ill-still .ill-head, .ill-still .ill-petal-r, .ill-still .ill-petal-c, .ill-still .ill-petal-l { transform-box: view-box; }
.ill-still .ill-head { transform-origin: 255px 158px; animation: still-head 5.1s ease-in-out infinite alternate; }
.ill-still .ill-arm-r { transform-origin: 273px 163px; animation: still-arm-r 3.4s ease-in-out infinite alternate; }
.ill-still .ill-arm-l { transform-origin: 237px 163px; animation: still-arm-l 3.9s ease-in-out infinite alternate; }
.ill-still .ill-leg { transform-origin: 274px 213px; animation: still-leg 4.4s ease-in-out infinite alternate; }
.ill-still .ill-gear { transform-box: fill-box; transform-origin: 50% 50%; animation: still-gear 9s linear infinite; }
.ill-still .ill-bulb { transform-box: fill-box; transform-origin: 50% 100%; animation: still-bulb 2.7s ease-in-out infinite alternate; }
.ill-still .ill-petal-r { transform-origin: 256px 58px; animation: still-petal-r 3.2s ease-in-out infinite alternate; }
.ill-still .ill-petal-l { transform-origin: 256px 58px; animation: still-petal-l 3.5s ease-in-out infinite alternate; }
.ill-still .ill-petal-c { transform-origin: 256px 58px; animation: still-petal-c 2.9s ease-in-out infinite alternate; }
@keyframes still-head { from { transform: rotate(-5deg); } to { transform: rotate(4deg); } }
@keyframes still-arm-r { from { transform: rotate(6deg); } to { transform: rotate(-7deg); } }
@keyframes still-arm-l { from { transform: rotate(-6deg); } to { transform: rotate(7deg); } }
@keyframes still-leg { from { transform: rotate(-3.5deg); } to { transform: rotate(0.5deg); } }
@keyframes still-gear { to { transform: rotate(360deg); } }
@keyframes still-bulb { from { transform: translateY(0) rotate(-6deg); } to { transform: translateY(-6px) rotate(6deg); } }
@keyframes still-petal-r { from { transform: rotate(0deg); } to { transform: rotate(11deg); } }
@keyframes still-petal-l { from { transform: rotate(0deg); } to { transform: rotate(-11deg); } }
@keyframes still-petal-c { from { transform: scale(0.96); } to { transform: scale(1.1); } }

.ill-focus .ill-head, .ill-focus .ill-arm-l, .ill-focus .ill-arm-r, .ill-focus .ill-crown, .ill-focus .ill-leaf-b, .ill-focus .ill-leaf-c, .ill-focus .ill-leaf-d, .ill-focus .ill-leaf-e { transform-box: view-box; }
.ill-focus .ill-head { transform-origin: 250px 214px; animation: focus-head 5.6s ease-in-out infinite alternate; }
.ill-focus .ill-breath { transform-box: fill-box; transform-origin: 50% 0%; animation: focus-breath 4.2s ease-in-out infinite alternate; }
.ill-focus .ill-arm-l { transform-origin: 229px 228px; animation: focus-arm-l 3.7s ease-in-out infinite alternate; }
.ill-focus .ill-arm-r { transform-origin: 271px 228px; animation: focus-arm-r 3.3s ease-in-out infinite alternate; }
.ill-focus .ill-crown { transform-origin: 411.8px 193.5px; animation: focus-crown 3.1s ease-in-out infinite alternate; }
.ill-focus .ill-leaf-d { transform-origin: 412.2px 216.8px; animation: focus-leaf-ccw 3.5s ease-in-out infinite alternate; animation-delay: -1.1s; }
.ill-focus .ill-leaf-c { transform-origin: 412.4px 225.3px; animation: focus-leaf-cw 3.8s ease-in-out infinite alternate; animation-delay: -0.7s; }
.ill-focus .ill-leaf-e { transform-origin: 411px 224.6px; animation: focus-leaf-ccw 4.4s ease-in-out infinite alternate; animation-delay: -2s; }
.ill-focus .ill-leaf-b { transform-origin: 412.6px 232.9px; animation: focus-leaf-cw 4.7s ease-in-out infinite alternate; animation-delay: -1.6s; }
@keyframes focus-head { from { transform: rotate(-4deg); } to { transform: rotate(3.5deg); } }
@keyframes focus-breath { from { transform: scale(1, 1); } to { transform: scale(1.035, 1.025); } }
@keyframes focus-arm-l { from { transform: rotate(0deg); } to { transform: rotate(4deg); } }
@keyframes focus-arm-r { from { transform: rotate(0deg); } to { transform: rotate(-4deg); } }
@keyframes focus-crown { from { transform: rotate(-3deg); } to { transform: rotate(3deg); } }
@keyframes focus-leaf-cw { from { transform: rotate(-3deg); } to { transform: rotate(6deg); } }
@keyframes focus-leaf-ccw { from { transform: rotate(3deg); } to { transform: rotate(-6deg); } }

.ill-duo .ill-cloud { animation: duo-cloud 9s ease-in-out infinite alternate; }
.ill-duo .ill-cloud-b { animation-duration: 7s; animation-direction: alternate-reverse; }
.ill-duo .ill-fern-a, .ill-duo .ill-fern-b, .ill-duo .ill-fern-c, .ill-duo .ill-fern-d, .ill-duo .ill-fern-e, .ill-duo .ill-fern-f { transform-box: fill-box; }
.ill-duo .ill-fern-a { transform-origin: 88% 100%; animation: duo-fern 3.4s ease-in-out infinite alternate; }
.ill-duo .ill-fern-b { transform-origin: 96% 100%; animation: duo-fern 4.1s ease-in-out -1s infinite alternate-reverse; }
.ill-duo .ill-fern-c { transform-origin: 20% 100%; animation: duo-fern 3.1s ease-in-out -0.5s infinite alternate; }
.ill-duo .ill-fern-d { transform-origin: 10% 100%; animation: duo-fern-big 2.7s ease-in-out -1.2s infinite alternate; }
.ill-duo .ill-fern-e { transform-origin: 45% 100%; animation: duo-fern 3.8s ease-in-out -2s infinite alternate-reverse; }
.ill-duo .ill-fern-f { transform-origin: 8% 100%; animation: duo-fern-big 4.4s ease-in-out -0.7s infinite alternate; }
.ill-duo .ill-him, .ill-duo .ill-his-head, .ill-duo .ill-her, .ill-duo .ill-her-head { transform-box: view-box; }
.ill-duo .ill-him { transform-origin: 350px 319px; animation: duo-breathe 4.6s ease-in-out infinite alternate; }
.ill-duo .ill-his-head { transform-origin: 349px 214px; animation: duo-his-head 4.6s ease-in-out infinite alternate; }
.ill-duo .ill-her { transform-origin: 150px 265px; animation: duo-breathe 5.4s ease-in-out -1.5s infinite alternate; }
.ill-duo .ill-her-head { transform-origin: 148px 168px; animation: duo-her-head 5.4s ease-in-out -1.5s infinite alternate; }
@keyframes duo-cloud { from { transform: translateX(-10px); } to { transform: translateX(12px); } }
@keyframes duo-fern { from { transform: rotate(-3deg); } to { transform: rotate(3deg); } }
@keyframes duo-fern-big { from { transform: rotate(-5deg); } to { transform: rotate(4deg); } }
@keyframes duo-breathe { from { transform: scaleY(1); } to { transform: scaleY(1.035); } }
@keyframes duo-his-head { from { transform: translateY(0) rotate(-3deg); } to { transform: translateY(-3.7px) rotate(4deg); } }
@keyframes duo-her-head { from { transform: translateY(0) rotate(3deg); } to { transform: translateY(-3.4px) rotate(-4deg); } }

.ill-serene .ill-fern-r, .ill-serene .ill-fern-o, .ill-serene .ill-fern-l { transform-box: fill-box; }
.ill-serene .ill-fern-r { transform-origin: 30% 100%; animation: serene-fern 3.6s ease-in-out infinite alternate; }
.ill-serene .ill-fern-o { transform-origin: 72% 100%; animation: serene-fern-o 4.3s ease-in-out -1.2s infinite alternate; }
.ill-serene .ill-fern-l { transform-origin: 88% 96%; animation: serene-fern 3.1s ease-in-out -0.8s infinite alternate-reverse; }
.ill-serene .ill-body, .ill-serene .ill-head, .ill-serene .ill-arm-r, .ill-serene .ill-arm-l { transform-box: view-box; }
.ill-serene .ill-body { transform-origin: 246px 365px; animation: serene-breathe 5s ease-in-out infinite alternate; }
.ill-serene .ill-head { transform-origin: 250px 262px; animation: serene-head 5s ease-in-out infinite alternate; }
.ill-serene .ill-arm-r { transform-origin: 300px 312px; animation: serene-arm-r 5s ease-in-out infinite alternate; }
.ill-serene .ill-arm-l { transform-origin: 192px 312px; animation: serene-arm-l 5s ease-in-out infinite alternate; }
@keyframes serene-fern { from { transform: rotate(-3deg); } to { transform: rotate(3deg); } }
@keyframes serene-fern-o { from { transform: rotate(-4deg); } to { transform: rotate(3deg); } }
@keyframes serene-breathe { from { transform: scaleY(1); } to { transform: scaleY(1.035); } }
@keyframes serene-head { from { transform: translateY(0) rotate(-3deg); } to { transform: translateY(-3.6px) rotate(3deg); } }
@keyframes serene-arm-r { from { transform: translateY(0) rotate(3deg); } to { transform: translateY(-1.8px) rotate(-5deg); } }
@keyframes serene-arm-l { from { transform: translateY(0) rotate(-3deg); } to { transform: translateY(-1.8px) rotate(5deg); } }

/* ── the second batch (2026-09-25): the same rules, one scoped block each ── */
.ill-schedule .ill-dot, .ill-schedule .ill-slot, .ill-schedule .ill-pin { transform-box: fill-box; transform-origin: 50% 50%; }
.ill-schedule .ill-dot { animation: schedule-dot 1.3s ease-in-out infinite alternate; }
.ill-schedule .ill-dot-b { animation-duration: 1.5s; animation-delay: -0.45s; }
.ill-schedule .ill-dot-c { animation-duration: 1.1s; animation-delay: -0.9s; }
.ill-schedule .ill-slot { animation: schedule-slot 2.3s ease-in-out infinite alternate; }
.ill-schedule .ill-slot-b { animation-duration: 2.9s; animation-delay: -1.1s; }
.ill-schedule .ill-slot-c { animation-duration: 3.5s; animation-delay: -2s; }
.ill-schedule .ill-pin { animation: schedule-pin 1.7s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-schedule .ill-reach, .ill-schedule .ill-nod, .ill-schedule .ill-hold { transform-box: view-box; }
.ill-schedule .ill-reach { transform-origin: 696px 624px; animation: schedule-reach 2.6s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-schedule .ill-nod { transform-origin: 656px 616px; animation: schedule-nod 3.8s ease-in-out infinite alternate; }
.ill-schedule .ill-hold { transform-origin: 580px 701px; animation: schedule-hold 3.1s ease-in-out infinite alternate; }
@keyframes schedule-dot { from { transform: scale(0.7); } to { transform: scale(1); } }
@keyframes schedule-slot { from { transform: scale(0.86); } to { transform: scale(1.1); } }
@keyframes schedule-pin { from { transform: scale(1) rotate(0deg); } to { transform: scale(1.12) rotate(-6deg); } }
@keyframes schedule-reach { from { transform: rotate(4deg); } to { transform: rotate(-9deg); } }
@keyframes schedule-nod { from { transform: rotate(-5deg); } to { transform: rotate(5deg); } }
@keyframes schedule-hold { from { transform: rotate(-4deg); } to { transform: rotate(5deg); } }

.ill-card .ill-chip, .ill-card .ill-field, .ill-card .ill-pins { transform-box: fill-box; }
.ill-card .ill-tap { transform-box: view-box; transform-origin: 913px 416.6px; animation: card-tap 1.1s ease-in-out infinite alternate; }
.ill-card .ill-tap-b { transform-origin: 887.8px 401px; animation-duration: 1.4s; animation-delay: -0.5s; }
.ill-card .ill-wink, .ill-card .ill-thumb { transform-box: view-box; }
.ill-card .ill-wink { transform-origin: 47.7px 146px; animation: card-wink 3.6s ease-in-out infinite; }
.ill-card .ill-chip { transform-origin: 50% 50%; animation: card-chip 2.4s ease-in-out infinite alternate; }
.ill-card .ill-pins { transform-origin: 50% 50%; animation: card-pins 1.6s ease-in-out infinite alternate; }
.ill-card .ill-pins-b { animation-duration: 1.9s; animation-delay: -0.8s; }
.ill-card .ill-field { transform-origin: 0% 50%; animation: card-field 2.9s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-card .ill-thumb { transform-origin: 728px 531.6px; animation: card-thumb 2.1s ease-in-out infinite alternate; }
@keyframes card-tap { from { transform: rotate(-3deg); } to { transform: rotate(4deg); } }
@keyframes card-wink { 0%, 86%, 100% { transform: scaleY(1); } 92% { transform: scaleY(0.12); } }
@keyframes card-chip { from { transform: scale(0.93); } to { transform: scale(1.06); } }
@keyframes card-pins { from { transform: scaleX(0.7); } to { transform: scaleX(1.08); } }
@keyframes card-field { from { transform: scaleX(0.82); } to { transform: scaleX(1); } }
@keyframes card-thumb { from { transform: rotate(-2deg); } to { transform: rotate(0.8deg); } }

.ill-organized .ill-head, .ill-organized .ill-note { transform-box: fill-box; }
.ill-organized .ill-head { transform-origin: 0% 50%; animation: organized-head 2.7s ease-in-out infinite alternate; }
.ill-organized .ill-head-b { animation-duration: 3.3s; animation-delay: -1.4s; }
.ill-organized .ill-head-c { animation-duration: 3.9s; animation-delay: -2.6s; }
.ill-organized .ill-note { transform-origin: 50% 0%; animation: organized-note 1.9s ease-in-out infinite alternate; }
.ill-organized .ill-note-b { animation-duration: 2.2s; animation-delay: -0.7s; }
.ill-organized .ill-note-c { animation-duration: 2.5s; animation-delay: -1.6s; }
.ill-organized .ill-note-d { animation-duration: 1.7s; animation-delay: -0.4s; }
.ill-organized .ill-note-e { animation-duration: 2.8s; animation-delay: -2.1s; }
.ill-organized .ill-place, .ill-organized .ill-swing { transform-box: view-box; }
.ill-organized .ill-place { transform-origin: 583px 237px; animation: organized-place 2.4s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-organized .ill-swing { transform-origin: 747px 223px; animation: organized-swing 3.4s ease-in-out infinite alternate; }
@keyframes organized-head { from { transform: scaleX(0.45); } to { transform: scaleX(1); } }
@keyframes organized-note { from { transform: rotate(-6deg); } to { transform: rotate(6deg); } }
@keyframes organized-place { from { transform: rotate(3deg); } to { transform: rotate(-7deg); } }
@keyframes organized-swing { from { transform: rotate(-3deg); } to { transform: rotate(2deg); } }

.ill-pitch .ill-nod, .ill-pitch .ill-type, .ill-pitch .ill-tick, .ill-pitch .ill-bar { transform-box: fill-box; }
.ill-pitch .ill-nod { transform-origin: 51% 73.3%; animation: pitch-nod 3.1s ease-in-out infinite alternate; }
.ill-pitch .ill-hair { transform-origin: 51.1% 138.8%; }
.ill-pitch .ill-type { transform-origin: 0% 50%; animation: pitch-type 2.3s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-pitch .ill-type-b { animation-duration: 2.7s; animation-delay: -0.9s; }
.ill-pitch .ill-type-c { animation-duration: 1.9s; animation-delay: -1.5s; }
.ill-pitch .ill-tick { transform-origin: 50% 50%; animation: pitch-tick 1.5s cubic-bezier(.3, 1.6, .5, 1) infinite alternate; }
.ill-pitch .ill-tick-b { animation-duration: 1.7s; animation-delay: -0.6s; }
.ill-pitch .ill-tick-c { animation-duration: 1.3s; animation-delay: -1.1s; }
.ill-pitch .ill-bar { transform-origin: 50% 100%; animation: pitch-bar 2.1s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-pitch .ill-bar-b { animation-duration: 2.5s; animation-delay: -0.4s; }
.ill-pitch .ill-bar-c { animation-duration: 1.8s; animation-delay: -1.2s; }
.ill-pitch .ill-bar-d { animation-duration: 2.9s; animation-delay: -0.7s; }
.ill-pitch .ill-bar-e { animation-duration: 3.4s; animation-delay: -2s; }
@keyframes pitch-nod { from { transform: rotate(-4deg); } to { transform: rotate(4deg); } }
@keyframes pitch-type { from { transform: scaleX(0.35); } to { transform: scaleX(1); } }
@keyframes pitch-tick { from { transform: scale(0.6); } to { transform: scale(1.12); } }
@keyframes pitch-bar { from { transform: scaleY(0.5); } to { transform: scaleY(1); } }

.ill-cleanup .ill-dot, .ill-cleanup .ill-line, .ill-cleanup .ill-flutter, .ill-cleanup .ill-nod, .ill-cleanup .ill-arm, .ill-cleanup .ill-cell { transform-box: fill-box; }
.ill-cleanup .ill-dot { transform-origin: 50% 50%; animation: cleanup-dot 1.7s ease-in-out infinite alternate; }
.ill-cleanup .ill-line { transform-origin: 0% 50%; animation: cleanup-line 2.6s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-cleanup .ill-line-b { animation-duration: 3.2s; animation-delay: -1.1s; }
.ill-cleanup .ill-cell { transform-origin: 50% 50%; animation: cleanup-cell 2.2s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-cleanup .ill-cell-b { animation-duration: 1.9s; animation-delay: -0.7s; }
.ill-cleanup .ill-cell-c { animation-duration: 2.8s; animation-delay: -1.6s; }
.ill-cleanup .ill-cell-d { animation-duration: 2.4s; animation-delay: -1.2s; }
.ill-cleanup .ill-flutter { transform-origin: 50% 50%; animation: cleanup-flutter 3.6s ease-in-out infinite alternate; }
.ill-cleanup .ill-flutter-b { animation-name: cleanup-flutter-b; animation-duration: 3s; animation-delay: -1.3s; }
.ill-cleanup .ill-flutter-c { animation-name: cleanup-flutter-c; animation-duration: 4.3s; animation-delay: -2.4s; }
.ill-cleanup .ill-nod { transform-origin: 57.7% 82%; animation: cleanup-nod 3.9s ease-in-out infinite alternate; }
.ill-cleanup .ill-hair { transform-origin: 33.6% 63.1%; }
.ill-cleanup .ill-arm { transform-origin: 83.3% 3.6%; animation: cleanup-arm 2.45s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-cleanup .ill-held { transform-origin: 353.1% 25.4%; }
@keyframes cleanup-dot { from { transform: scale(0.8); } to { transform: scale(1.15); } }
@keyframes cleanup-line { from { transform: scaleX(0.45); } to { transform: scaleX(1); } }
@keyframes cleanup-cell { from { transform: scale(0.7); } to { transform: scale(1.04); } }
@keyframes cleanup-flutter { from { transform: translate(-6px, -10px) rotate(-10deg); } to { transform: translate(6px, 10px) rotate(8deg); } }
@keyframes cleanup-flutter-b { from { transform: translate(8px, -8px) rotate(9deg); } to { transform: translate(-6px, 12px) rotate(-8deg); } }
@keyframes cleanup-flutter-c { from { transform: translate(-10px, 4px) rotate(-7deg); } to { transform: translate(2px, 10px) rotate(7deg); } }
@keyframes cleanup-nod { from { transform: rotate(-3deg); } to { transform: rotate(4deg); } }
@keyframes cleanup-arm { from { transform: rotate(-5deg); } to { transform: rotate(4deg); } }

.ill-stripepay .ill-nod, .ill-stripepay .ill-wave, .ill-stripepay .ill-badge, .ill-stripepay .ill-tick, .ill-stripepay .ill-coin, .ill-stripepay .ill-bar, .ill-stripepay .ill-ping { transform-box: fill-box; }
.ill-stripepay .ill-nod { transform-origin: 60.6% 94.7%; animation: stripepay-nod 3.3s ease-in-out infinite alternate; }
.ill-stripepay .ill-wave { transform-origin: 81.9% 9.6%; animation: stripepay-wave 2.2s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-stripepay .ill-badge { transform-origin: 50% 50%; animation: stripepay-badge 2.9s ease-in-out infinite alternate; }
.ill-stripepay .ill-tick { transform-origin: 40% 60%; animation: stripepay-tick 1.4s cubic-bezier(.3, 1.6, .5, 1) infinite alternate; }
.ill-stripepay .ill-tick-b { animation-duration: 1.6s; animation-delay: -0.5s; }
.ill-stripepay .ill-tick-c { animation-duration: 1.25s; animation-delay: -0.9s; }
.ill-stripepay .ill-coin { animation: stripepay-coin 1.1s cubic-bezier(.3, 0, .6, 1) infinite alternate; }
.ill-stripepay .ill-coin-b { animation-duration: 1.35s; animation-delay: -0.4s; }
.ill-stripepay .ill-coin-c { animation-duration: 0.95s; animation-delay: -0.7s; }
.ill-stripepay .ill-bar { transform-origin: 0% 50%; animation: stripepay-bar 2.5s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-stripepay .ill-bar-b { animation-duration: 3.1s; animation-delay: -1.2s; }
.ill-stripepay .ill-bar-c { animation-duration: 2.7s; animation-delay: -0.6s; }
.ill-stripepay .ill-ping { transform-origin: 50% 50%; animation: stripepay-ping 1.8s ease-in-out infinite alternate; }
.ill-stripepay .ill-ping-b { animation-duration: 2.05s; animation-delay: -0.8s; }
.ill-stripepay .ill-ping-c { animation-duration: 1.55s; animation-delay: -1.3s; }
@keyframes stripepay-nod { from { transform: rotate(-5deg); } to { transform: rotate(6deg); } }
@keyframes stripepay-wave { from { transform: rotate(-7deg); } to { transform: rotate(6deg); } }
@keyframes stripepay-badge { from { transform: scale(0.82); } to { transform: scale(1.06); } }
@keyframes stripepay-tick { from { transform: scale(0.55); } to { transform: scale(1.12); } }
@keyframes stripepay-coin { from { transform: translateY(3px); } to { transform: translateY(-9px); } }
@keyframes stripepay-bar { from { transform: scaleX(0.4); } to { transform: scaleX(1); } }
@keyframes stripepay-ping { from { transform: scale(0.8); } to { transform: scale(1.5); } }

.ill-booked .ill-plant, .ill-booked .ill-tick, .ill-booked .ill-string { transform-box: fill-box; }
.ill-booked .ill-board { animation: booked-swing 4.6s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-booked .ill-string { transform-origin: 50% 0%; animation: booked-string 4.6s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-booked .ill-tick { transform-origin: 28% 92%; animation: booked-tick-swing 4.6s cubic-bezier(.45, 0, .55, 1) infinite alternate, booked-tick 3.3s ease-in-out infinite; }
.ill-booked .ill-tick-b { animation-delay: 0s, -2.2s; }
.ill-booked .ill-tick-c { animation-delay: 0s, -1.1s; }
.ill-booked .ill-plant { transform-origin: 50% 100%; animation: booked-plant 3.7s ease-in-out infinite alternate; }
.ill-booked .ill-nod { transform-box: view-box; transform-origin: 640px 268px; animation: booked-nod 2.9s ease-in-out infinite alternate; }
.ill-booked .ill-arm { transform-box: view-box; transform-origin: 650px 302px; animation: booked-arm 2.3s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
@keyframes booked-swing { from { transform: translateX(0); } to { transform: translateX(11px); } }
@keyframes booked-tick-swing { from { translate: 0 0; } to { translate: 11px 0; } }
@keyframes booked-string { from { transform: rotate(0deg); } to { transform: rotate(-6.5deg); } }
@keyframes booked-tick { 0%, 62%, 100% { scale: 1; } 74% { scale: 1.28; } 84% { scale: 0.94; } 92% { scale: 1.04; } }
@keyframes booked-plant { from { transform: rotate(-5deg); } to { transform: rotate(4deg); } }
@keyframes booked-nod { from { transform: rotate(-4deg); } to { transform: rotate(5deg); } }
@keyframes booked-arm { from { transform: rotate(-7deg); } to { transform: rotate(5deg); } }

.ill-rain .ill-bill, .ill-rain .ill-bill-top, .ill-rain .ill-whoosh { transform-box: fill-box; transform-origin: 50% 50%; }
.ill-rain .ill-bill { animation: rain-bill 3.1s ease-in-out infinite alternate; }
.ill-rain .ill-bill-b { animation-duration: 3.7s; animation-delay: -1.2s; animation-name: rain-bill-r; }
.ill-rain .ill-bill-c { animation-duration: 2.7s; animation-delay: -0.6s; }
.ill-rain .ill-bill-d { animation-duration: 4.1s; animation-delay: -2.4s; animation-name: rain-bill-r; }
.ill-rain .ill-bill-e { animation-duration: 3.4s; animation-delay: -1.9s; }
.ill-rain .ill-bill-f { animation-duration: 2.9s; animation-delay: -0.3s; animation-name: rain-bill-r; }
.ill-rain .ill-bill-g { animation-duration: 3.9s; animation-delay: -2.9s; }
.ill-rain .ill-bill-top { animation: rain-bill-top 3.3s ease-in-out infinite alternate; }
.ill-rain .ill-bill-top-b { animation-duration: 4.4s; animation-delay: -1.7s; }
.ill-rain .ill-hold { transform-box: view-box; transform-origin: 1357.97px 395.37px; animation: rain-hold 2.5s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-rain .ill-flick { transform-box: view-box; transform-origin: 1476.97px 382.37px; animation: rain-flick 1.1s cubic-bezier(.5, 0, .3, 1) infinite alternate; }
.ill-rain .ill-whoosh { transform-origin: 100% 50%; animation: rain-whoosh 1.1s cubic-bezier(.5, 0, .3, 1) infinite alternate; }
.ill-rain .ill-nod { transform-box: view-box; transform-origin: 1425.97px 355.37px; animation: rain-nod 2.1s ease-in-out infinite alternate; }
@keyframes rain-bill { from { transform: translate(-6px, -9px) rotate(-12deg); } to { transform: translate(7px, 10px) rotate(10deg); } }
@keyframes rain-bill-r { from { transform: translate(7px, -8px) rotate(11deg); } to { transform: translate(-6px, 11px) rotate(-13deg); } }
@keyframes rain-bill-top { from { transform: translate(-5px, 9px) rotate(-8deg); } to { transform: translate(6px, 26px) rotate(9deg); } }
@keyframes rain-hold { from { transform: rotate(-4deg); } to { transform: rotate(3deg); } }
@keyframes rain-flick { from { transform: rotate(-3deg); } to { transform: rotate(6deg); } }
@keyframes rain-whoosh { from { transform: translateX(6px) scaleX(0.7); } to { transform: translateX(-4px) scaleX(1.1); } }
@keyframes rain-nod { from { transform: rotate(-3deg); } to { transform: rotate(4deg); } }

.ill-place .ill-pin { animation: place-pin 1.3s cubic-bezier(.55, 0, 1, .45) infinite alternate; }
.ill-place .ill-shadow { transform-box: fill-box; transform-origin: 50% 50%; animation: place-shadow 1.3s cubic-bezier(.55, 0, 1, .45) infinite alternate; }
.ill-place .ill-float { animation: place-float 5.2s ease-in-out infinite alternate; }
.ill-place .ill-float-b { animation-duration: 4.3s; animation-delay: -2.1s; animation-name: place-float-b; }
.ill-place .ill-float-c { animation-duration: 6.1s; animation-delay: -3.4s; }
.ill-place .ill-tree, .ill-place .ill-sprout { transform-box: fill-box; transform-origin: 50% 100%; }
.ill-place .ill-tree { animation: place-tree 3.1s ease-in-out infinite alternate; }
.ill-place .ill-tree-b { animation-duration: 3.8s; animation-delay: -1.5s; }
.ill-place .ill-sprout { animation: place-sprout 2.3s ease-in-out infinite alternate; }
@keyframes place-pin { from { transform: translateY(0); } to { transform: translateY(15px); } }
@keyframes place-shadow { from { transform: scaleX(0.82); } to { transform: scaleX(1.08); } }
@keyframes place-float { from { transform: translate(-4px, -6px); } to { transform: translate(5px, 8px); } }
@keyframes place-float-b { from { transform: translate(5px, 6px); } to { transform: translate(-3px, -7px); } }
@keyframes place-tree { from { transform: rotate(-3.5deg); } to { transform: rotate(3deg); } }
@keyframes place-sprout { from { transform: rotate(-9deg); } to { transform: rotate(7deg); } }

.ill-shop .ill-dot, .ill-shop .ill-tick { transform-box: fill-box; transform-origin: 50% 50%; }
.ill-shop .ill-sign, .ill-shop .ill-point, .ill-shop .ill-nod, .ill-shop .ill-cake, .ill-shop .ill-hold, .ill-shop .ill-arrow { transform-box: view-box; }
.ill-shop .ill-sign { transform-origin: 145.5px 295px; animation: shop-sign 2.3s ease-in-out infinite alternate; }
.ill-shop .ill-sign-b { transform-origin: 264px 355px; animation-duration: 2.7s; animation-direction: alternate-reverse; }
.ill-shop .ill-point { transform-origin: 445px 182px; animation: shop-point 1.9s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-shop .ill-nod { transform-origin: 468px 157px; animation: shop-nod 3.3s ease-in-out infinite alternate; }
.ill-shop .ill-hold { transform-origin: 490px 180px; animation: shop-hold 2.9s ease-in-out infinite alternate; }
.ill-shop .ill-cake { animation: shop-cake 1.6s ease-in-out infinite alternate; }
.ill-shop .ill-cake-a { transform-origin: 183px 328px; animation-duration: 1.45s; }
.ill-shop .ill-cake-b { transform-origin: 237px 368px; animation-duration: 1.75s; animation-direction: alternate-reverse; }
.ill-shop .ill-cake-c { transform-origin: 235px 323px; animation-duration: 2.05s; }
.ill-shop .ill-cake-d { transform-origin: 183px 370px; animation-duration: 1.2s; animation-direction: alternate-reverse; }
.ill-shop .ill-dot { animation: shop-dot 2.5s ease-in-out infinite; }
.ill-shop .ill-dot-b { animation-duration: 3.6s; animation-delay: -1.2s; }
.ill-shop .ill-tick { animation: shop-tick 2.1s ease-in-out infinite; }
.ill-shop .ill-arrow { transform-origin: 238px 288px; animation: shop-arrow 3.9s ease-in-out infinite alternate; }
.ill-shop .ill-arrow-b { transform-origin: 411px 79px; animation-duration: 4.4s; animation-direction: alternate-reverse; }
@keyframes shop-sign { from { transform: rotate(-8deg); } to { transform: rotate(8deg); } }
@keyframes shop-point { from { transform: rotate(-7deg); } to { transform: rotate(5deg); } }
@keyframes shop-nod { from { transform: rotate(-5deg); } to { transform: rotate(4deg); } }
@keyframes shop-hold { from { transform: rotate(-5deg); } to { transform: rotate(3deg); } }
@keyframes shop-cake { from { transform: rotate(-7deg); } to { transform: rotate(7deg); } }
@keyframes shop-dot { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.18); } }
@keyframes shop-tick { 0%, 50%, 100% { transform: scale(1); } 65% { transform: scale(1.35); } 80% { transform: scale(0.92); } }
@keyframes shop-arrow { from { transform: rotate(-10deg); } to { transform: rotate(8deg); } }

.ill-calendar .ill-dot, .ill-calendar .ill-line, .ill-calendar .ill-note, .ill-calendar .ill-cell { transform-box: fill-box; }
.ill-calendar .ill-cell { transform-origin: 50% 50%; animation: calendar-cell 3.6s ease-in-out infinite; }
.ill-calendar .ill-cell-b { animation-duration: 4.2s; animation-delay: -1.4s; }
.ill-calendar .ill-cell-c { animation-duration: 3.2s; animation-delay: -2.3s; }
.ill-calendar .ill-head, .ill-calendar .ill-lift, .ill-calendar .ill-pin { transform-box: view-box; }
.ill-calendar .ill-head { transform-origin: 110px 72px; animation: calendar-head 3.4s ease-in-out infinite alternate; }
.ill-calendar .ill-lift { transform-origin: 165px 106px; animation: calendar-lift 2.2s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-calendar .ill-card { transform-origin: -140.5px -37.2px; }
.ill-calendar .ill-dot { transform-origin: 50% 50%; animation: calendar-dot 2.6s ease-in-out infinite; }
.ill-calendar .ill-line { transform-origin: 0% 50%; animation: calendar-line 3.8s ease-in-out infinite alternate; }
.ill-calendar .ill-line-b { animation-duration: 2.9s; animation-delay: -1.1s; }
.ill-calendar .ill-note { transform-origin: 50% 0%; animation: calendar-note 1.9s ease-in-out infinite alternate; }
.ill-calendar .ill-note-b { animation-duration: 2.4s; animation-direction: alternate-reverse; }
.ill-calendar .ill-note-c { animation-duration: 1.55s; animation-delay: -0.6s; }
.ill-calendar .ill-note-d { animation-duration: 2.75s; animation-direction: alternate-reverse; animation-delay: -0.9s; }
.ill-calendar .ill-pin { transform-origin: 20.8px 1px; animation: calendar-note 3.1s ease-in-out infinite alternate-reverse; }
.ill-calendar .ill-pin-lines { transform-origin: 11.4px -6.9px; }
@keyframes calendar-head { from { transform: rotate(-4deg); } to { transform: rotate(4deg); } }
@keyframes calendar-lift { from { transform: rotate(0deg); } to { transform: rotate(7deg); } }
@keyframes calendar-dot { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.25); } }
@keyframes calendar-line { from { transform: scaleX(0.55); } to { transform: scaleX(1); } }
@keyframes calendar-cell { 0%, 100% { transform: scale(0.7); } 50% { transform: scale(1.12); } }
@keyframes calendar-note { from { transform: rotate(-7deg); } to { transform: rotate(7deg); } }

.ill-checks .ill-plant, .ill-checks .ill-tick, .ill-checks .ill-new { transform-box: fill-box; }
.ill-checks .ill-nod, .ill-checks .ill-swing, .ill-checks .ill-tap { transform-box: view-box; }
.ill-checks .ill-plant { transform-origin: 57% 100%; animation: checks-plant 4.6s ease-in-out infinite alternate; }
.ill-checks .ill-tick { transform-origin: 50% 50%; animation: checks-tick 2.2s ease-in-out infinite; }
.ill-checks .ill-new { transform-origin: 50% 50%; animation: checks-new 5.4s cubic-bezier(.3, 1.4, .5, 1) infinite; }
.ill-checks .ill-new-c { animation-delay: -4.4s; }
.ill-checks .ill-nod { transform-origin: 865px 326px; animation: checks-nod 3.1s ease-in-out infinite alternate; }
.ill-checks .ill-tap { transform-origin: 832px 346px; animation: checks-tap 1.7s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-checks .ill-swing { transform-origin: 905px 356px; animation: checks-swing 2.6s ease-in-out infinite alternate; }
@keyframes checks-plant { from { transform: skewX(-10deg); } to { transform: skewX(10deg); } }
@keyframes checks-tick { 0%, 55%, 100% { transform: scale(1); } 70% { transform: scale(1.22); } 82% { transform: scale(0.94); } }
@keyframes checks-new { 0%, 22% { transform: scale(0); } 34%, 86% { transform: scale(1); } 96%, 100% { transform: scale(0); } }
@keyframes checks-nod { from { transform: rotate(0deg); } to { transform: rotate(4deg); } }
@keyframes checks-tap { from { transform: rotate(-7deg); } to { transform: rotate(4deg); } }
@keyframes checks-swing { from { transform: rotate(-6deg); } to { transform: rotate(4deg); } }

/* budget */
.ill-budget .ill-bar, .ill-budget .ill-knob, .ill-budget .ill-coin, .ill-budget .ill-icon { transform-box: fill-box; }
.ill-budget .ill-bar { transform-origin: 50% 100%; }
.ill-budget .ill-bar-a { animation: budget-bar-a 2.3s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-budget .ill-knob-a { animation: budget-knob-a 2.3s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-budget .ill-reach { animation: budget-reach 2.3s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-budget .ill-bar-b { animation: budget-bar-b 3.1s cubic-bezier(.45, 0, .55, 1) -1.2s infinite alternate; }
.ill-budget .ill-knob-b { animation: budget-knob-b 3.1s cubic-bezier(.45, 0, .55, 1) -1.2s infinite alternate; }
.ill-budget .ill-bar-c { animation: budget-bar-c 3.7s cubic-bezier(.45, 0, .55, 1) -2.5s infinite alternate; }
.ill-budget .ill-knob-c { animation: budget-knob-c 3.7s cubic-bezier(.45, 0, .55, 1) -2.5s infinite alternate; }
.ill-budget .ill-nod { transform-box: view-box; transform-origin: 801.5px 559px; animation: budget-nod 4.3s ease-in-out infinite alternate; }
.ill-budget .ill-coin { transform-origin: 50% 50%; animation: budget-coin 1.9s ease-in-out infinite; }
.ill-budget .ill-coin-b { animation-duration: 2.6s; animation-delay: -0.9s; }
.ill-budget .ill-coin-c { animation-duration: 2.1s; animation-delay: -1.5s; }
.ill-budget .ill-icon { transform-origin: 50% 100%; animation: budget-icon 1.7s ease-in-out infinite; }
.ill-budget .ill-icon-b { animation-duration: 2.0s; animation-delay: -0.6s; }
.ill-budget .ill-icon-c { animation-duration: 2.4s; animation-delay: -1.3s; }
@keyframes budget-bar-a { from { transform: scaleY(1); } to { transform: scaleY(1.0611); } }
@keyframes budget-knob-a { from { transform: translateY(0); } to { transform: translateY(-16px); } }
@keyframes budget-reach { from { transform: translate(0, 0); } to { transform: translate(6.8px, -16px); } }
@keyframes budget-bar-b { from { transform: scaleY(0.6); } to { transform: scaleY(1.1); } }
@keyframes budget-knob-b { from { transform: translateY(44px); } to { transform: translateY(-11px); } }
@keyframes budget-bar-c { from { transform: scaleY(0.8); } to { transform: scaleY(1.03); } }
@keyframes budget-knob-c { from { transform: translateY(63.2px); } to { transform: translateY(-9.48px); } }
@keyframes budget-nod { from { transform: rotate(-4deg); } to { transform: rotate(4deg); } }
@keyframes budget-coin { 0%, 100% { transform: translateY(0) scale(1); } 50% { transform: translateY(-8px) scale(1.18); } }
@keyframes budget-icon { 0%, 60%, 100% { transform: translateY(0) scaleY(1); } 30% { transform: translateY(-9px) scaleY(1.04); } }

/* payonline */
.ill-payonline .ill-dot, .ill-payonline .ill-streak, .ill-payonline .ill-stripe, .ill-payonline .ill-chip, .ill-payonline .ill-logo, .ill-payonline .ill-sway { transform-box: fill-box; }
.ill-payonline .ill-dot { transform-origin: 50% 0%; animation: payonline-dot 1.5s ease-in-out infinite; }
.ill-payonline .ill-dot-b { animation-duration: 1.6s; animation-delay: -1.1s; }
.ill-payonline .ill-dot-c { animation-duration: 1.7s; animation-delay: -0.6s; }
.ill-payonline .ill-streak { transform-origin: 100% 50%; animation: payonline-streak 1.9s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-payonline .ill-streak-b { animation-duration: 2.3s; animation-delay: -0.7s; }
.ill-payonline .ill-streak-c { animation-duration: 2.05s; animation-delay: -1.4s; }
.ill-payonline .ill-streak-d { animation-duration: 2.6s; animation-delay: -0.3s; }
.ill-payonline .ill-stripe { transform-origin: 0% 50%; animation: payonline-stripe 2.8s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-payonline .ill-chip { transform-origin: 50% 50%; animation: payonline-chip 3.2s ease-in-out infinite; }
.ill-payonline .ill-logo { transform-origin: 50% 50%; animation: payonline-logo 2.2s ease-in-out -0.5s infinite; }
.ill-payonline .ill-nod { transform-box: view-box; transform-origin: 467px 166px; animation: payonline-nod 3.6s ease-in-out infinite alternate; }
.ill-payonline .ill-sway { transform-origin: 37% 100%; animation: payonline-sway 4.1s ease-in-out infinite alternate; }
@keyframes payonline-dot { 0%, 50%, 100% { transform: scale(1); } 25% { transform: scale(1.35); } }
@keyframes payonline-streak { from { transform: scaleX(0.45); } to { transform: scaleX(1); } }
@keyframes payonline-stripe { from { transform: scaleX(0.35); } to { transform: scaleX(1); } }
@keyframes payonline-chip { 0%, 70%, 100% { transform: scale(1) rotate(0deg); } 80% { transform: scale(1.14) rotate(-4deg); } 90% { transform: scale(1.06) rotate(3deg); } }
@keyframes payonline-logo { 0%, 100% { transform: scale(1); } 40% { transform: scale(1.22); } 60% { transform: scale(0.94); } }
@keyframes payonline-nod { from { transform: rotate(-6deg); } to { transform: rotate(5deg); } }
@keyframes payonline-sway { from { transform: rotate(-10deg); } to { transform: rotate(0deg); } }

/* mobilepay */
.ill-mobilepay .ill-pin, .ill-mobilepay .ill-hop, .ill-mobilepay .ill-card, .ill-mobilepay .ill-float { transform-box: fill-box; }
.ill-mobilepay .ill-pin { transform-origin: 50% 50%; animation: mobilepay-pin 2.4s ease-in-out infinite; }
.ill-mobilepay .ill-pin-b { animation-duration: 2.5s; animation-delay: -1.9s; }
.ill-mobilepay .ill-pin-c { animation-duration: 2.6s; animation-delay: -1.3s; }
.ill-mobilepay .ill-pin-d { animation-duration: 2.7s; animation-delay: -0.7s; }
.ill-mobilepay .ill-hop { animation: mobilepay-hop 1.1s ease-in-out infinite; }
.ill-mobilepay .ill-hop-b { animation-duration: 1.2s; animation-delay: -0.8s; }
.ill-mobilepay .ill-hop-c { animation-duration: 1.3s; animation-delay: -0.4s; }
.ill-mobilepay .ill-card { transform-origin: 50% 50%; animation: mobilepay-card 3.4s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
.ill-mobilepay .ill-float { transform-origin: 50% 50%; animation: mobilepay-float 4.6s ease-in-out infinite alternate; }
.ill-mobilepay .ill-float-b { animation-name: mobilepay-float-b; animation-duration: 3.9s; animation-delay: -1.7s; }
.ill-mobilepay .ill-arm-l, .ill-mobilepay .ill-arm-r, .ill-mobilepay .ill-nod { transform-box: view-box; }
.ill-mobilepay .ill-arm-l { transform-origin: 641px 372px; animation: mobilepay-arm-l 2.9s ease-in-out infinite alternate; }
.ill-mobilepay .ill-arm-r { transform-origin: 711px 376px; animation: mobilepay-arm-r 3.1s ease-in-out infinite alternate; }
.ill-mobilepay .ill-nod { transform-origin: 670px 345px; animation: mobilepay-nod 3.7s ease-in-out infinite alternate; }
@keyframes mobilepay-pin { 0%, 40%, 100% { transform: scale(1); } 20% { transform: scale(1.5); } }
@keyframes mobilepay-hop { 0%, 60%, 100% { transform: translateY(0); } 30% { transform: translateY(-4px); } }
@keyframes mobilepay-card { from { transform: translateY(6px) rotate(-2deg); } to { transform: translateY(-8px) rotate(2deg); } }
@keyframes mobilepay-float { from { transform: translateY(8px) rotate(-8deg); } to { transform: translateY(-12px) rotate(8deg); } }
@keyframes mobilepay-float-b { from { transform: translateY(10px); } to { transform: translateY(-14px); } }
@keyframes mobilepay-arm-l { from { transform: rotate(-3deg); } to { transform: rotate(7deg); } }
@keyframes mobilepay-arm-r { from { transform: rotate(4deg); } to { transform: rotate(-6deg); } }
@keyframes mobilepay-nod { from { transform: rotate(-5deg); } to { transform: rotate(6deg); } }

.ill-search .ill-twinkle, .ill-search .ill-spin, .ill-search .ill-shade, .ill-search .ill-pin, .ill-search .ill-orb, .ill-search .ill-lens { transform-box: fill-box; }
.ill-search .ill-ring { transform-box: view-box; transform-origin: 1102px 717px; animation: search-ring 6.5s ease-in-out infinite alternate; }
.ill-search .ill-pin { transform-origin: 50% 100%; animation: search-pin 2.6s cubic-bezier(.45, 0, .25, 1) infinite; }
.ill-search .ill-shade { transform-origin: 50% 50%; animation: search-shade 2.6s cubic-bezier(.45, 0, .25, 1) infinite; }
.ill-search .ill-pin-l, .ill-search .ill-shade-l { animation-duration: 3.1s; animation-delay: -1.2s; }
.ill-search .ill-pin-r, .ill-search .ill-shade-r { animation-duration: 2.2s; animation-delay: -0.5s; }
.ill-search .ill-lens { transform-origin: 96% 96%; animation: search-lens 5.2s ease-in-out infinite; }
.ill-search .ill-twinkle { transform-origin: 50% 50%; animation: search-twinkle 1.9s ease-in-out infinite; }
.ill-search .ill-twinkle-b { animation-duration: 2.3s; animation-delay: -0.9s; }
.ill-search .ill-twinkle-c { animation-duration: 1.7s; animation-delay: -1.3s; }
.ill-search .ill-spin { transform-origin: 50% 50%; animation: search-spin 4.4s ease-in-out infinite alternate; }
.ill-search .ill-spin-b { animation-duration: 3.7s; animation-direction: alternate-reverse; }
.ill-search .ill-orb { animation: search-orb 3.4s ease-in-out infinite alternate; }
.ill-search .ill-orb-b { animation-duration: 2.8s; animation-delay: -1.1s; }
@keyframes search-ring { from { transform: rotate(-4deg); } to { transform: rotate(4deg); } }
@keyframes search-pin { 0%, 55%, 100% { transform: translateY(0); } 25% { transform: translateY(-22px); } 40% { transform: translateY(0) scaleY(.94); } }
@keyframes search-shade { 0%, 55%, 100% { transform: scale(1); } 25% { transform: scale(.6); } }
@keyframes search-lens { 0%, 100% { transform: translate(0, 0) rotate(0); } 25% { transform: translate(-30px, -16px) rotate(-6deg); } 50% { transform: translate(-12px, -34px) rotate(0); } 75% { transform: translate(16px, -18px) rotate(5deg); } }
@keyframes search-twinkle { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.6); } }
@keyframes search-spin { from { transform: rotate(0); } to { transform: rotate(180deg); } }
@keyframes search-orb { from { transform: translateY(0); } to { transform: translateY(-14px); } }

.ill-timeline .ill-leaf, .ill-timeline .ill-card, .ill-timeline .ill-hang, .ill-timeline .ill-tap, .ill-timeline .ill-hair { transform-box: fill-box; }
.ill-timeline .ill-leaf { transform-origin: 50% 100%; animation: timeline-leaf 4.6s ease-in-out infinite alternate; }
.ill-timeline .ill-leaf-b { animation-name: timeline-leaf-b; animation-duration: 3.3s; animation-delay: -1.5s; }
.ill-timeline .ill-card { transform-origin: 50% 100%; animation: timeline-card 3.8s ease-in-out infinite alternate; }
.ill-timeline .ill-card-b { animation-name: timeline-card-b; animation-duration: 4.3s; animation-delay: -2s; }
.ill-timeline .ill-arrow { animation: timeline-arrow 1.8s ease-in-out infinite; }
.ill-timeline .ill-arrow-b { animation-name: timeline-arrow-b; animation-duration: 2.1s; animation-delay: -0.7s; }
.ill-timeline .ill-tap { transform-origin: 89% 75%; animation: timeline-tap 1.5s cubic-bezier(.45, 0, .25, 1) infinite alternate; }
.ill-timeline .ill-hang { transform-origin: 55% 3%; animation: timeline-hang 3.6s ease-in-out infinite alternate; }
.ill-timeline .ill-hair { transform-origin: 31% 6%; animation: timeline-hair 2.9s ease-in-out infinite alternate; }
@keyframes timeline-leaf { from { transform: skewX(0); } to { transform: skewX(7deg); } }
@keyframes timeline-leaf-b { from { transform: skewX(-6deg); } to { transform: skewX(6deg); } }
@keyframes timeline-card { from { transform: translateY(0) rotate(0); } to { transform: translateY(-14px) rotate(3deg); } }
@keyframes timeline-card-b { from { transform: translateY(0); } to { transform: translateY(14px); } }
@keyframes timeline-arrow { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(9px, -5px); } }
@keyframes timeline-arrow-b { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(10px, 5px); } }
@keyframes timeline-tap { from { transform: rotate(-7deg); } to { transform: rotate(5deg); } }
@keyframes timeline-hang { from { transform: rotate(-5deg); } to { transform: rotate(4deg); } }
@keyframes timeline-hair { from { transform: rotate(-4deg); } to { transform: rotate(3deg); } }

/* The reveal. The resting state is VISIBLE: this hidden state exists only
   behind html.anim, which only the script adds, so no JS, a crawler and
   reduced motion all get the finished page. And it is on the drawings ONLY,
   never on anything holding text: the browser suite's contrast sweep skips an
   element at opacity 0, so a text reveal would quietly take everything below
   the fold out of the contrast check while the suite stayed green. The loops
   wait for the reveal, so a drawing never arrives mid-swing. */
html.anim .ill.reveal { transition: opacity 800ms cubic-bezier(.2, .7, .2, 1), transform 800ms cubic-bezier(.2, .7, .2, 1); }
html.anim .ill.reveal:not(.seen) { opacity: 0; transform: translateY(18px); }
html.anim .ill.reveal:not(.seen) * { animation-play-state: paused; }

/* ── the enquiry form ──────────────────────────────────────────────────── */
/* On the pine band, so every colour here is against --pine and the label ink
   is --pine-tint rather than --ink-3. That pair is the one check:contrast
   already holds; --ink-3 on pine measures 2.19:1 and is the exact mistake the
   hero eyebrow made once. */
.enquiry { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4) var(--s5); margin-top: var(--s7); max-width: 44rem; }
.field { display: flex; flex-direction: column; gap: var(--s2); }
.enquiry .wide { grid-column: 1 / -1; }
.enquiry label { font-size: 14px; font-weight: 600; color: var(--pine-tint); }
.enquiry input {
  font: inherit;
  font-size: 16px; /* under 16px iOS zooms the whole page on focus */
  color: var(--ink);
  background: var(--sheet);
  border: 1px solid var(--sheet);
  border-radius: 8px;
  padding: var(--s3) var(--s4);
  width: 100%;
}
.enquiry input:focus-visible { outline: 3px solid var(--pine-tint); outline-offset: 2px; }
.enquiry button { margin-top: var(--s2); }
/* Off-screen rather than display:none — a bot reading the DOM skips what is
   hidden, and the point is that it fills this one in. */
.trap { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

.form-note { color: var(--pine-tint); font-size: 15px; margin: var(--s2) 0 0; min-height: 1.4em; }
.enquiry.sending { opacity: 0.6; }
/* The whole point of submitting in the background: the fields GO, and what is
   left is the answer. Leaving five filled inputs on screen under a line of
   thank-you text reads as though it might not have sent, which is the doubt
   this was built to remove. The .form-note paragraph is the one child that
   survives. */
.enquiry.sent > *:not(.form-note) { display: none; }
.enquiry.sent .form-note { font-size: 17px; color: var(--sheet); font-weight: 600; max-width: 34rem; }

/* ── footer ────────────────────────────────────────────────────────────── */
/* Pine-deep, the ground the rule near the top of this sheet already gave it:
   a second footer rule painting plain pine made it the same flat colour as the
   enquiry band above, with a hard step where that band's lit corner meets it. */
footer { color: var(--pine-tint); padding: var(--s8) 0 var(--s7); }
.foot-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s6); }
.foot-grid h2 { font-size: 13px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; color: var(--sheet); margin: 0 0 var(--s3); }
.foot-grid ul { list-style: none; margin: 0; padding: 0; font-size: 15px; }
.foot-grid li { margin-bottom: var(--s2); }
.foot-grid a:hover { color: var(--sheet); }
.colophon { margin-top: var(--s7); padding-top: var(--s5); border-top: 1px solid rgba(223, 233, 227, 0.3); font-size: 14px; }

@media (max-width: 900px) {
  .hero-grid, .rooms, .trio, .price-grid, .checks, .foot-grid, .faq { grid-template-columns: 1fr; gap: var(--s6); }
  /* Cleared explicitly: a span of 2 over a single-column grid leaves a hole. */
  .rooms .room:first-child { grid-row: auto; }
  .rooms .room:first-child h3 { font-size: 1.3rem; }
  /* The ledger keeps its rows on a phone but drops the number column, which
     would otherwise eat a third of a 390px line. */
  .trio.four .third { grid-template-columns: 1fr; gap: var(--s2); }
  .trio.four .third::before { font-size: 1.5rem; }
  /* No overhang on a phone: a 104px pull on a 390px column reads as a
     collision rather than as layering. */
  .overhang { padding-top: var(--s8); }
  .overhang .figure-gap { margin-top: 0; }
  .enquiry { grid-template-columns: 1fr; gap: var(--s4); }
  .band { padding: var(--s8) 0; }
  .topnav { gap: var(--s4); font-size: 14px; }
  .topnav a.hide-sm { display: none; }
  /* The drawing drops under its heading and keeps to the right, at a size
     that leaves a 390px column its text. */
  .headrow { grid-template-columns: minmax(0, 1fr); row-gap: var(--s5); }
  .headrow > div { padding-bottom: 0; }
  .headrow .ill { justify-self: end; width: min(62%, 230px); }
  .headrow + .trio.four, .headrow + .faq { margin-top: var(--s6); }
  .ill-calm { margin-bottom: 0; }
}

/* ═══ the multi-page layer ════════════════════════════════════════════════ */
/* Everything below arrived with the second page. Same tokens, same type, same
   rules: a drawing sits on a light ground or on a pine-tint stage and never
   straight on pine, where every dark in it becomes the ground; no drawing
   stands beside a floor plan; motion is transform only. */

/* A button is a button in the page's face. Only the form inputs inherited the
   font, so the one real button on the page, the enquiry submit, rendered in
   the browser default: Arial on a Mac, beside Grotesk everywhere else. */
.btn { font: inherit; font-weight: 600; font-size: 16px; }

/* Masthead: four studio links, two buying links, one button. The current page
   is marked by an underline drawn as a shadow, so nothing about its colour
   eases. Under 900px the links fold into a details element that needs no
   script. */
.top .wrap { flex-wrap: nowrap; }
.mark { color: var(--sheet); }
.topnav .sep { width: 1px; height: 18px; background: var(--pine-lit); }
.topnav a[aria-current] { color: var(--sheet); }
.menu { display: none; }

/* Links inside a running answer: underlined, because colour alone is not a
   link, and in the ink the paragraph already uses. */
.inline { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.inline-light { color: var(--sheet); text-decoration: underline; text-underline-offset: 3px; }

/* Hero: ONE scene instead of two stickers. The seat map is the subject, so it
   is the only object at full contrast and the only one that crosses the
   ground's edge onto pine; the drawing stands inside a pine-tint arch and the
   card covers the far end of her mat, which is what makes the two read as one
   room rather than a picture beside a widget. The owner's verdict on the
   first version, which put a disc and the card side by side: "I think we can
   do much better here". The arch keeps the class .disc because the unit test
   holds every drawing on a pine band to a disc, card-stage or stage holder:
   on pine every dark in a drawing turns into the ground. */
.hero.layered { padding: var(--s8) 0 var(--s9); overflow: clip; }
.hero.layered .hero-grid { grid-template-columns: minmax(0, 11fr) minmax(0, 10fr); gap: var(--s7); align-items: center; }
/* Three lines at 1280 and 1440, not four. The copy column is 536px at 1280 and
   "Booking software" at 3.9rem is about 500px, so the break is Booking
   software / for boutique / fitness studios. */
.hero.layered h1 { font-size: clamp(2.5rem, 5vw, 3.9rem); max-width: 18ch; line-height: 1.02; }
.hero.layered .lede { max-width: 30rem; }
.hero.layered .actions { margin-top: var(--s6); }
.scene { position: relative; padding-bottom: var(--s8); }
/* The drafting grid is --rule, not --tex: --tex on pine-tint is 1.03:1 and
   vanishes. A floor, not a pattern: the plane a layout is set out on. */
.scene .disc {
  position: relative; width: 84%; aspect-ratio: 20 / 21; border-radius: 999px 999px 28px 28px;
  background-color: var(--pine-tint);
  background-image: radial-gradient(circle at 1px 1px, var(--rule) 1.1px, rgba(210, 218, 213, 0) 1.6px);
  background-size: 22px 22px; background-position: 11px 11px;
  overflow: hidden;
}
/* left: 0, not negative: the arch clips, and at 390 a negative offset cut off
   her back foot. */
.scene .disc .ill { position: absolute; left: 0; bottom: 9%; width: 72%; }
/* A sheet behind, turned, then the live card. They share one grid cell so the
   sheet is always exactly the card's size, whatever the seat key wraps to.
   Three shadows on the card (contact, near, far) because one blurred shadow
   reads as a glow, not as a card resting on something. */
.stack { position: absolute; right: var(--s6); bottom: 0; width: min(18rem, 60%); display: grid; }
.stack > * { grid-area: 1 / 1; }
.stack .frag {
  position: relative;
  box-shadow: 0 1px 2px rgba(10, 42, 34, 0.18), 0 10px 18px rgba(10, 42, 34, 0.16), 0 34px 60px rgba(10, 42, 34, 0.34);
}
.ghost {
  border-radius: 16px; background: var(--paper);
  transform: rotate(5deg) translate(6%, -5%); transform-origin: 0% 100%;
  box-shadow: 0 10px 26px rgba(10, 42, 34, 0.26);
}
/* A lede under a section heading needs air the paragraph rule does not give:
   h2 carries no bottom margin because most of them head a grid. */
:is(h1, h2) + .lede { margin-top: var(--s4); }
.split h2 + .price-list, .split h2 + .ticks { margin-top: var(--s5); }

/* Hero B, a studio page: paper with the drafting grid, and the drawing
   standing on a 2px ink floor that runs the width of the page. */
.hero-b { padding: var(--s8) 0; }
/* Top-aligned, under a masthead and a padding that never change, so the frame
   starts at the same height on every inner page however long the heading. */
.hero-b .wrap { display: grid; grid-template-columns: 6fr 5fr; gap: var(--s7); align-items: start; }
.hero-b h1 { max-width: 16ch; font-size: clamp(2.3rem, 5vw, 3.6rem); }
.hero-b .lede { margin-top: var(--s5); }
/* The frame: one size everywhere, the drawing centred in it at its own
   proportions (an svg's default preserveAspectRatio), so a wide drawing and a
   tall one occupy the same box and the page does not jump between them. */
.hero-b .frame { width: 100%; max-width: 440px; height: 360px; justify-self: end; display: flex; }
.hero-b .frame .ill { width: 100%; height: 100%; }

/* Split: text beside a drawing, alternating sides down a page. */
.split { display: grid; grid-template-columns: 6fr 5fr; gap: var(--s8); align-items: center; }
.split.flip { grid-template-columns: 5fr 6fr; }
.split.flip > .art { order: -1; }
.split .art { display: flex; justify-content: center; }
.split .art .ill { width: 100%; max-width: 400px; }
/* A split whose drawing has not arrived yet: one column at the reading
   measure, never a text column beside an empty one. */
.split.solo { grid-template-columns: minmax(0, 44rem); }
.split .lede { margin-top: var(--s4); }
.ticks { list-style: none; padding: 0; margin: var(--s5) 0 0; }
.ticks li { padding: var(--s3) 0; border-top: 1px solid var(--rule); font-size: 16px; color: var(--ink-2); }
.ticks li b { color: var(--ink); font-weight: 600; }
.more { display: inline-flex; gap: var(--s2); align-items: center; font-weight: 600; margin-top: var(--s5); color: var(--pine); border-bottom: 2px solid var(--pine); padding-bottom: 2px; }
.actions .more { margin-top: 0; }
.more .arr, .studio .arr { display: inline-block; }
.plan-big svg { display: block; width: 100%; height: auto; background: var(--sheet); border-radius: 12px; box-shadow: 0 1px 2px rgba(18, 32, 27, 0.05), 0 8px 24px rgba(18, 32, 27, 0.06); }

/* The rooms, drawn with people in them. Transform only, one drawing per
   scope, and each keyframe named for its drawing, the same rules as the
   illustrations. The delays stagger the room so it never moves in step. */
.ill-reformers .ill-glide { animation: reformers-glide 2.6s ease-in-out infinite alternate; }
.ill-reformers .ill-legs { transform-box: fill-box; transform-origin: 50% 100%; animation: reformers-legs 2.6s ease-in-out infinite alternate; }
@keyframes reformers-glide { from { transform: translateY(0); } to { transform: translateY(-7px); } }
@keyframes reformers-legs { from { transform: scaleY(1); } to { transform: scaleY(1.169); } }
.ill-bikes .ill-rock { animation: bikes-rock 0.55s ease-in-out infinite alternate; }
@keyframes bikes-rock { from { transform: translateX(-1.4px); } to { transform: translateX(1.4px); } }
.ill-barre .ill-arm { transform-box: fill-box; transform-origin: 0 0; animation: barre-arm 3.2s ease-in-out infinite alternate; }
@keyframes barre-arm { from { transform: rotate(0deg); } to { transform: rotate(-150deg); } }
.ill-mats .ill-pose { transform-box: fill-box; transform-origin: 50% 96%; animation: mats-pose 3.8s ease-in-out infinite alternate; }
.ill-mats .ill-p2 { animation-delay: -1.3s; }
.ill-mats .ill-p3 { animation-delay: -2.6s; }
@keyframes mats-pose { from { transform: rotate(-2deg); } to { transform: rotate(2deg); } }
/* Last, so no loop's shorthand above resets them. */
.ill-reformers .d1, .ill-bikes .d1, .ill-barre .d1 { animation-delay: -0.9s; }
.ill-reformers .d2, .ill-bikes .d2, .ill-barre .d2 { animation-delay: -1.7s; }
.ill-reformers .d3, .ill-bikes .d3, .ill-barre .d3 { animation-delay: -2.3s; }

/* Studio cards: a drawing on a tinted stage, then the words. The card lifts
   and its shadow deepens on hover; no colour moves.

   The LINK is the card's last line, not the card. A whole-card link is a
   control, and a control's boundary is held to 3:1 against its ground — a
   white card on paper is 1.12:1, which the browser suite's boundary sweep
   refused on its first run. The line's ::after is stretched over the card, so
   the whole card still takes the click while the thing that IS the control is
   text, measured as text. */
.studios { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s5); margin-top: var(--s7); }
.studio {
  position: relative;
  display: flex; flex-direction: column; background: var(--sheet); color: var(--ink);
  border-radius: 16px; overflow: hidden;
  box-shadow: 0 1px 2px rgba(18, 32, 27, 0.05), 0 8px 24px rgba(18, 32, 27, 0.06);
}
.stage { background: var(--pine-tint); height: 190px; display: flex; align-items: flex-end; justify-content: center; padding: var(--s4) var(--s4) 0; }
.stage .ill { height: 170px; width: auto; max-width: 100%; }
.studio .body { padding: var(--s4) var(--s5) var(--s5); }
.studio h3 { font-size: 1.15rem; margin-bottom: var(--s2); }
.studio p { font-size: 15px; color: var(--ink-2); margin: 0 0 var(--s3); }
.studio .go { font-size: 15px; font-weight: 600; color: var(--pine); white-space: nowrap; }
.studio .go::after { content: ''; position: absolute; inset: 0; border-radius: 16px; }

/* The enquiry band: the form beside a drawing on a pine-tint card, which is
   the one way a drawing reads on pine. */
.cta-grid { display: grid; grid-template-columns: 7fr 4fr; gap: var(--s8); align-items: center; }
.cta-grid .enquiry { max-width: none; }
.card-stage { background: var(--pine-tint); border-radius: 20px; padding: var(--s6) var(--s5) var(--s4); display: flex; align-items: flex-end; }
.card-stage .ill { width: 100%; }


/* The bars on the ledger card fill once as the drawing arrives. The GROUP
   grows, not each bar: every bar carries its own transform attribute, and a
   CSS transform REPLACES that attribute rather than composing with it — the
   first version animated the bars themselves and sent all five sliding 250
   units across the card. The held state is a scale behind html.anim, never an
   opacity, so with no script the bars are simply full. */
.ill-fillbar { transform-box: fill-box; transform-origin: 0 50%; }
html.anim .ill.reveal .ill-fillbar { transition: transform 1100ms cubic-bezier(.2, .7, .2, 1) 300ms; }
html.anim .ill.reveal:not(.seen) .ill-fillbar { transform: scaleX(0.12); }

@media (max-width: 900px) {
  .topnav { display: none; }
  .menu { display: block; margin-left: auto; position: relative; }
  .menu summary { list-style: none; cursor: pointer; color: var(--sheet); font-weight: 600; font-size: 15px; border: 2px solid var(--pine-tint); border-radius: 999px; padding: 7px var(--s4); }
  .menu summary::-webkit-details-marker { display: none; }
  .menu nav { position: absolute; right: 0; top: calc(100% + var(--s2)); background: var(--pine-deep); border-radius: 12px; padding: var(--s3) var(--s5); display: grid; gap: var(--s2); min-width: 220px; z-index: 5; }
  .menu nav a { color: var(--pine-tint); padding: var(--s1) 0; }
  .menu nav a[aria-current] { color: var(--sheet); }
  .menu nav .sep { height: 1px; background: var(--pine-lit); }
  .hero-b { padding: var(--s6) 0 var(--s7); }
  .hero-b .wrap { grid-template-columns: 1fr; gap: var(--s5); }
  /* Drawing FIRST on a phone: below the heading its height would follow the
     heading's length, and the picture would move between pages — the thing
     the owner asked not to happen. Above it, nothing before it varies. */
  .hero-b .frame { max-width: 320px; height: 240px; justify-self: center; order: -1; }
  .split, .split.flip { grid-template-columns: 1fr; gap: var(--s5); }
  .split .art { order: -1; justify-content: flex-start; }
  .split .art .ill { max-width: 260px; }
  .studios { grid-template-columns: 1fr; gap: var(--s4); }
  .stage { height: 150px; }
  .stage .ill { height: 136px; }
  .studio .body { padding: var(--s4); }
  .cta-grid { grid-template-columns: 1fr; gap: var(--s6); }
  .card-stage { order: -1; max-width: 280px; padding: var(--s4) var(--s4) var(--s3); }
  .hero.layered { padding: var(--s7) 0 var(--s8); }
  .hero.layered .hero-grid { grid-template-columns: minmax(0, 1fr); gap: var(--s7); }
  .scene { max-width: 26rem; padding-bottom: 150px; }
  .scene .disc { width: 88%; }
  .scene .disc .ill { width: 76%; }
  .stack { right: var(--s2); width: min(15.5rem, 66%); }
  .ghost { transform: rotate(4deg) translate(3%, -4%); }
  /* On the contact page the heading is the page; the drawing waits below it. */
  .cta-grid.page .card-stage { order: 0; }
}


/* ═══ motion between and around the words ═════════════════════════════════ */
/* The owner: "transitions in general are less catchy to the eye". So text and
   cards now arrive — fade and rise, headings clearing a blur, rows sliding in,
   cards lifting from a slight scale — staggered 70ms apart as they enter
   together, and hover is something you feel: buttons lift on a spring, cards
   rise 8px and their drawing with them, nav underlines grow from the left.

   The one recorded rule this CHANGES, and why it is still safe. Hiding text
   before its reveal was forbidden because the browser suite's contrast sweep
   skips anything at opacity 0: a sweep that loaded the page and never
   scrolled would quietly measure only the first screen. The sweep now
   scrolls the whole page first, fails if any reveal never fired, and reports
   any text it skipped — see testing-practice. The resting state is still the
   visible state: the hidden state lives behind html.anim, which only the
   script adds, and inside a no-preference query, so no JS, reduced motion and
   print all get the finished page.

   Keyframes rather than transitions for the reveals: transition is one
   property, so a reveal transition on a card would replace the card's own
   hover transition and make the lift ease at reveal speed. Each reveal names
   only its FROM frame and fills backwards, so its end is the element's live
   value and hover still stacks on top. No colour eases except the two the
   button rule already clears; everything else is transform and shadow. */
@media screen and (prefers-reduced-motion: no-preference) {
  html.anim .rv:not(.seen) { opacity: 0; }
}
html.anim .rv.seen { animation: rv-rise 820ms cubic-bezier(.16, 1, .3, 1) backwards; }
html.anim h1.rv.seen, html.anim h2.rv.seen { animation-name: rv-head; animation-duration: 1000ms; }
html.anim .studio.rv.seen, html.anim .check.rv.seen, html.anim .qa.rv.seen, html.anim .plan-big.rv.seen, html.anim figure.rv.seen, html.anim .card-stage.rv.seen, html.anim .scene.rv.seen { animation-name: rv-card; animation-duration: 950ms; }
html.anim .third.rv.seen, html.anim .price-list li.rv.seen, html.anim .ticks li.rv.seen { animation-name: rv-row; }
html.anim .scene.rv.seen .ghost { animation: rv-fan 1100ms cubic-bezier(.16, 1, .3, 1) 520ms backwards; }
html.anim .scene.rv.seen .stack .frag { animation: rv-drop 1000ms cubic-bezier(.16, 1, .3, 1) 380ms backwards; }
@keyframes rv-rise { from { opacity: 0; transform: translate3d(0, 26px, 0); } }
@keyframes rv-head { from { opacity: 0; transform: translate3d(0, 34px, 0); filter: blur(8px); } }
@keyframes rv-card { from { opacity: 0; transform: translate3d(0, 44px, 0) scale(.965); } }
@keyframes rv-row { from { opacity: 0; transform: translate3d(-22px, 0, 0); } }
@keyframes rv-fan { from { transform: rotate(0deg) translate(0, 0); } }
@keyframes rv-drop { from { transform: translate3d(0, -26px, 0) rotate(-2.5deg); } }

.btn:not(.line) { transition: background-color 200ms ease, color 200ms ease, transform 220ms cubic-bezier(.34, 1.56, .64, 1), box-shadow 220ms ease; }
.btn.line { transition: transform 220ms cubic-bezier(.34, 1.56, .64, 1), box-shadow 220ms ease; }
.btn:hover { transform: translateY(-2px); box-shadow: 0 2px 4px rgba(10, 42, 34, 0.14), 0 12px 24px rgba(10, 42, 34, 0.22); }
.on-pine .btn:hover { box-shadow: 0 2px 4px rgba(5, 20, 16, 0.3), 0 14px 28px rgba(5, 20, 16, 0.45); }
.btn:active { transform: translateY(0); box-shadow: 0 1px 2px rgba(10, 42, 34, 0.2); transition-duration: 90ms; }

.studio { transition: transform 320ms cubic-bezier(.2, .7, .2, 1), box-shadow 320ms ease; }
.studio:hover, .studio:focus-within { transform: translateY(-8px); box-shadow: 0 4px 8px rgba(18, 32, 27, 0.06), 0 28px 56px rgba(18, 32, 27, 0.16); }
.studio .stage .ill { transition: transform 420ms cubic-bezier(.34, 1.56, .64, 1); }
html.anim .studio .stage .ill.reveal.seen { transition: opacity 800ms cubic-bezier(.2, .7, .2, 1), transform 420ms cubic-bezier(.34, 1.56, .64, 1); }
.studio:hover .stage .ill, .studio:focus-within .stage .ill { transform: translateY(-6px) scale(1.04); }
.more .arr, .studio .arr { transition: transform 260ms cubic-bezier(.34, 1.56, .64, 1); }
.more:hover .arr, .studio:hover .arr, .studio:focus-within .arr { transform: translateX(6px); }

/* A pseudo-element underline, not a gradient on the link: the contrast sweep
   treats a background image on a text element as that text's ground. */
.topnav a:not(.btn), .foot-grid a { position: relative; transition: color 200ms ease; }
.topnav a:not(.btn)::after, .foot-grid a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -3px; height: 2px;
  background: var(--pine-tint); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 300ms cubic-bezier(.2, .7, .2, 1);
}
.foot-grid a::after { height: 1px; bottom: -2px; }
.topnav a:not(.btn):hover::after, .foot-grid a:hover::after, .topnav a[aria-current]::after { transform: scaleX(1); }
.inline, .inline-light { text-decoration-thickness: 1px; transition: text-decoration-thickness 200ms ease, text-underline-offset 200ms ease; }
.inline:hover, .inline-light:hover { text-decoration-thickness: 2px; text-underline-offset: 5px; }

