/* ==========================================================================
   JuiceMount — SCENE SYSTEM.  Layers on site.css to give the long scroll
   real rhythm without feeling infinite: distinct "scenes" with a consistent
   LEFT eyeline, an isometric-depth backdrop (echoes the hero's 3D stack), and
   a "blocks paging in" seam between scenes — the product's own cache-fill
   mechanic, fired ONCE as each seam scrolls into view (never looping).

   Brand-true: cinema-dark / rind-cream / juice-green, flat + outlined.
   Used with restraint: the iso grid is faint and not on every scene; the seam
   sweep is a one-time reveal, then it rests quiet. Retune from this file.
   ========================================================================== */

:root {
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  --measure: 46rem;
  --scene-pad: clamp(var(--space-8), 3rem + 4vw, 7rem);

  /* isometric-depth backdrop — two faint 30deg line sets, projected like the
     hero stack. --iso-line adapts per theme/band. */
  --iso-line: rgba(27, 16, 42, 0.08);
  --scene-alt-wash: rgba(27, 16, 42, 0.055);
  /* a square grid tilted 30deg (perpendicular line families) so the static
     backdrop matches the hero shader's iso block-field, not a 60deg rhombus. */
  --iso-bg:
    repeating-linear-gradient(30deg, var(--iso-line) 0 1px, transparent 1px 30px),
    repeating-linear-gradient(120deg, var(--iso-line) 0 1px, transparent 1px 30px);
  --iso-mask: radial-gradient(135% 92% at 80% 22%, #000 26%, transparent 82%);

  /* the block seam: dim "uncached" at rest, faint juice-green once paged in. */
  --seam-rest: rgba(27, 16, 42, 0.10);
  --seam-cached: rgba(170, 205, 88, 0.22);
}
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --iso-line: rgba(250,253,232,0.07); --scene-alt-wash: rgba(250,253,232,0.045); --seam-rest: rgba(250,253,232,0.10); } }
[data-theme="dark"] { --iso-line: rgba(250,253,232,0.07); --scene-alt-wash: rgba(250,253,232,0.045); --seam-rest: rgba(250,253,232,0.10); }
.band-ink { --iso-line: rgba(250, 253, 232, 0.07); --scene-alt-wash: rgba(250, 253, 232, 0.045); --seam-rest: rgba(250, 253, 232, 0.10); }

/* tinted panel — a perceptibly different light surface, alternated with the
   clean cream scenes so the background alone separates sections (no dividers).
   The wash fades IN at the top edge and OUT at the bottom so the panel melts
   into the cream above and below instead of ending on a hard line (blend). */
.scene--alt {
  background-image: linear-gradient(
    180deg,
    transparent 0%,
    var(--scene-alt-wash) 11%,
    var(--scene-alt-wash) 89%,
    transparent 100%
  );
}

/* --------------------------------------------------------------------------
   SCENE — every section is a positioned stage that carries backdrop layers
   behind its content: ::before = iso grid, ::after = film grain.
   -------------------------------------------------------------------------- */
.scene { position: relative; isolation: isolate; }
.scene > .container,
.scene > * { position: relative; z-index: 1; }
.scene::before,
.scene::after { content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none; }

/* iso-depth backdrop (opt-in, faint, masked to fade up-and-right). */
.scene--iso::before {
  background: var(--iso-bg);
  -webkit-mask: var(--iso-mask);
          mask: var(--iso-mask);
}
/* film grain (dark scenes get a touch more). */
.scene--grain::after { background-image: var(--grain); background-size: 160px 160px; opacity: 0.035; mix-blend-mode: soft-light; }
.band-ink.scene--grain::after { opacity: 0.06; mix-blend-mode: overlay; }

/* cinematic glow folded into the dark band's own background (frees both
   pseudos for iso + grain). */
.band-ink.scene--glow {
  background:
    radial-gradient(70% 120% at var(--gx, 78%) var(--gy, 26%), rgba(170, 205, 88, 0.08), transparent 55%),
    radial-gradient(120% 120% at 50% -10%, #20242c 0%, var(--cinema-dark) 62%);
  transition: --gx 0.5s var(--ease), --gy 0.5s var(--ease);
}

/* tonal wash — a quiet step off the base for a light scene that follows
   another light scene (no iso, so the run still has variation). */
.scene--wash::before {
  background:
    linear-gradient(180deg, var(--surface-2) 0%, transparent 24%),
    radial-gradient(120% 60% at 50% 122%, var(--surface-2), transparent 60%);
}

/* --------------------------------------------------------------------------
   SEAM — the "blocks paging in" divider. A row of blocks, dim (uncached) at
   rest; when scrolled into view scenes.js adds .is-in and they page in green
   left-to-right (one-time), settling to a faint cached tint. Replaces folds.
   -------------------------------------------------------------------------- */
.seam {
  display: flex;
  gap: 5px;
  max-width: var(--container);
  margin-inline: auto;
  padding-block: var(--scene-seam, var(--space-7));
  padding-inline: var(--space-4);
}
.seam i {
  flex: 1 1 0;
  height: 11px;
  border-radius: 2px;
  background: var(--seam-rest);
  transform: translateY(0);
}
.seam.is-in i {
  animation: seamPage 0.5s var(--ease) backwards;
  animation-delay: calc(var(--n, 0) * 0.03s);
  background: var(--seam-cached);
}
@keyframes seamPage {
  0%   { background: var(--seam-rest);   opacity: 0.4; transform: translateY(3px); }
  55%  { background: var(--accent);      opacity: 1;   transform: translateY(0); }
  100% { background: var(--seam-cached); opacity: 1;   transform: translateY(0); }
}
@media (min-width: 1280px) { .seam { padding-inline: var(--space-6); } }
@media (prefers-reduced-motion: reduce) {
  .seam i { transition: none; }
  .seam.is-in i { animation: none; background: var(--seam-cached); }
}

/* --------------------------------------------------------------------------
   EYELINE — all section copy hangs off the container's left edge; media is
   LEFT-anchored to that same edge (never center-floated). Hero / closing opt
   out via .scene--center.
   -------------------------------------------------------------------------- */
.scene .section-head { max-width: var(--measure); margin-inline: 0; }
.stage { margin-inline: 0; margin-block: var(--space-6) 0; max-width: 100%; }
.stage--measure { max-width: var(--measure); }
.scene--center { text-align: center; }
.scene--center .section-head,
.scene--center .container > p { margin-inline: auto; }
.scene--center .stage { margin-inline: auto; }


/* --------------------------------------------------------------------------
   SPLIT — repeating feature layout: copy left, visual right, one baseline.
   -------------------------------------------------------------------------- */
.split { display: grid; gap: var(--space-6); align-items: center; }
.split > .split-copy { max-width: var(--measure); }
.split > .split-media { min-width: 0; }
.split--top { align-items: start; }
@media (min-width: 900px) {
  .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); gap: var(--space-8); }
  .split--media-left > .split-media { order: -1; }
}

/* --------------------------------------------------------------------------
   STICKY / CAPTIVE — a visual that pins while supporting beats scroll past.
   Use inside .split--top. The single biggest "don't overuse" item: at most
   one captive beat per page.
   -------------------------------------------------------------------------- */
@media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
  .sticky-figure { position: sticky; top: clamp(var(--space-6), 12vh, var(--space-9)); }
}
.sticky-beats { display: grid; gap: var(--space-7); }
.sticky-beats .beat { max-width: 34rem; }
.sticky-beats .beat .label { display: block; margin-bottom: var(--space-2); }
.sticky-beats .beat h3 { margin-bottom: var(--space-2); }
.sticky-beats .beat p { color: var(--text-muted); margin: 0; }
@media (prefers-reduced-motion: reduce) { .sticky-figure { position: static; } }

/* --------------------------------------------------------------------------
   STAGE FRAME — a consistent panel for a demo that reads better framed.
   -------------------------------------------------------------------------- */
.stage-frame { border: 1.5px solid var(--hairline-soft); border-radius: var(--radius); background: var(--surface); padding: var(--space-5); }
.band-ink .stage-frame { background: rgba(250, 253, 232, 0.02); border-color: var(--hairline-soft); }

/* --------------------------------------------------------------------------
   Hero gets extra top air; the finale a longer tail.
   -------------------------------------------------------------------------- */
.scene--hero { padding-block: clamp(var(--space-8), 6vw, var(--space-9)) var(--scene-pad); }
.scene--finale { padding-block: var(--scene-pad) clamp(var(--space-9), 10vw, 8rem); }

/* --------------------------------------------------------------------------
   SPINE + PROGRESS — wayfinding. The single biggest "stop feeling infinite"
   lever: a visible denominator. A hairline scroll-progress rail (how much is
   left) + a fixed left-margin section index with a live "NN / NN" counter and
   a "you are here" tick (scroll-spy in scenes.js). Neutral slate reads on both
   the cream and cinema-dark scenes; juice-green marks the active scene.
   -------------------------------------------------------------------------- */
.scroll-progress { position: fixed; inset: 0 0 auto 0; height: 2px; z-index: 60; pointer-events: none; background: transparent; }
.scroll-progress::before { content: ""; display: block; height: 100%; background: var(--juice-green); transform: scaleX(0); transform-origin: left; }
@supports (animation-timeline: scroll()) {
  .scroll-progress::before { animation: sp-grow linear both; animation-timeline: scroll(root); }
  @keyframes sp-grow { to { transform: scaleX(1); } }
}

.spine { position: fixed; left: clamp(14px, 2.4vw, 34px); top: 50%; transform: translateY(-50%); z-index: 55; display: flex; flex-direction: column; gap: 13px; }
.spine-count { font: 600 10px/1 var(--font-mono); letter-spacing: 0.1em; color: rgba(136, 135, 134, 0.85); margin-bottom: 5px; }
.spine a { display: flex; align-items: center; gap: 9px; text-decoration: none; }
.spine .dot { width: 7px; height: 7px; border-radius: 2px; background: rgba(136, 135, 134, 0.45); transition: background 0.25s var(--ease), width 0.25s var(--ease), height 0.25s var(--ease); }
.spine .lab { font: 600 10px/1 var(--font-mono); letter-spacing: 0.07em; text-transform: uppercase; color: rgba(136, 135, 134, 0.8); opacity: 0; transform: translateX(-5px); transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), color 0.2s var(--ease); white-space: nowrap; }
.spine a:hover .lab, .spine a.is-active .lab { opacity: 1; transform: none; }
.spine a:hover .dot { background: rgba(136, 135, 134, 0.85); }
.spine a.is-active .dot { background: var(--accent); width: 9px; height: 9px; }
.spine a.is-active .lab { color: var(--accent); }
.spine a:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; border-radius: 2px; }
@media (max-width: 1120px) { .spine { display: none; } }
@media (prefers-reduced-motion: reduce) { .spine .dot, .spine .lab { transition: none; } }

/* --------------------------------------------------------------------------
   HINGE — a scroll-into tonal dissolve at the one cream->dark boundary, so the
   scenes read as one reel instead of stacked pages. Theme-robust: uses --bg
   (cream in light, cinema-dark in dark, where it becomes a silent no-op).
   -------------------------------------------------------------------------- */
.hinge { height: clamp(110px, 15vh, 180px); background: linear-gradient(180deg, var(--bg) 0%, var(--cinema-dark) 100%); margin-bottom: calc(-1 * var(--space-4)); }

/* ==========================================================================
   BLEND & REVEAL — the "one continuous reel" layer (inspired by sites whose
   sections melt into each other): content rises + fades in as it scrolls into
   view, hovering a card lifts it while its neighbours recede (eye-guiding
   spotlight), and the dark bands carry a soft green glow that follows the
   cursor. All of it is opt-in via the .js flag (no-JS keeps full content) and
   silenced under prefers-reduced-motion. Retune the feel from these tokens.
   ========================================================================== */
:root {
  --reveal-rise: 18px;
  --reveal-dur: 0.72s;
  --reveal-ease: cubic-bezier(0.16, 0.62, 0.23, 0.99); /* long, soft settle */
  --reveal-step: 80ms;                                  /* stagger between siblings */
  --spot: rgba(170, 205, 88, 0.5);                      /* hovered-card accent edge */
}

/* register the glow centre so it can EASE back on pointer-leave (snaps without). */
@property --gx { syntax: "<percentage>"; inherits: false; initial-value: 78%; }
@property --gy { syntax: "<percentage>"; inherits: false; initial-value: 26%; }

@media (prefers-reduced-motion: no-preference) {
  /* ---- scroll reveal: hide leaf content units, then .is-in settles them in.
     Leaf units = each direct child of a scene's .container, EXCEPT the card
     grids, whose individual cards are revealed (and staggered) instead. The
     hero opts out — it has its own intro below. --ri is the per-parent index
     scenes.js stamps for the stagger. -------------------------------------- */
  .js .scene:not(.scene--hero) > .container > :not(.bp):not(.sw-vs):not(.pillars),
  .js .scene:not(.scene--hero) > .container > .bp > .bp-card,
  .js .scene:not(.scene--hero) > .container > .sw-vs > .sw-vs-card,
  .js .scene:not(.scene--hero) > .container > .pillars > .card {
    opacity: 0;
    transform: translateY(var(--reveal-rise));
    transition: opacity var(--reveal-dur) var(--reveal-ease),
                transform var(--reveal-dur) var(--reveal-ease);
    transition-delay: calc(var(--ri, 0) * var(--reveal-step));
    will-change: opacity, transform;
  }
  /* revealed state — these mirror the hidden selectors + .is-in so they carry
     one more class of specificity and reliably win. */
  .js .scene:not(.scene--hero) > .container > :not(.bp):not(.sw-vs):not(.pillars).is-in,
  .js .scene:not(.scene--hero) > .container > .bp > .bp-card.is-in,
  .js .scene:not(.scene--hero) > .container > .sw-vs > .sw-vs-card.is-in,
  .js .scene:not(.scene--hero) > .container > .pillars > .card.is-in {
    opacity: 1;
    transform: none;
    will-change: auto;
  }

  /* ---- hero intro: the copy column breathes in once on load (CSS-only). ---- */
  .js .scene--hero .hero-copy > * {
    animation: heroIn var(--reveal-dur) var(--reveal-ease) both;
  }
  .js .scene--hero .hero-copy > :nth-child(1) { animation-delay: 0.04s; }
  .js .scene--hero .hero-copy > :nth-child(2) { animation-delay: 0.12s; }
  .js .scene--hero .hero-copy > :nth-child(3) { animation-delay: 0.20s; }
  .js .scene--hero .hero-copy > :nth-child(4) { animation-delay: 0.28s; }
  .js .scene--hero .hero-copy > :nth-child(5) { animation-delay: 0.36s; }
  @keyframes heroIn {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
  }
}

/* --------------------------------------------------------------------------
   HOVER SPOTLIGHT — guide the eye: hovering one card in a group lifts it a
   touch and dims its neighbours, so attention follows the cursor. Pure
   progressive enhancement (no :has() support → no dimming, no harm).
   -------------------------------------------------------------------------- */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .bp .bp-card,
  .sw-vs .sw-vs-card,
  .pillars .card {
    transition: opacity 0.32s var(--ease), transform 0.32s var(--ease),
                border-color 0.32s var(--ease), background-color 0.32s var(--ease);
  }
  .bp:has(.bp-card:hover) .bp-card:not(:hover),
  .sw-vs:has(.sw-vs-card:hover) .sw-vs-card:not(:hover),
  .pillars:has(.card:hover) .card:not(:hover) {
    opacity: 0.46;
  }
  .bp-card:hover,
  .sw-vs-card:hover,
  .pillars .card:hover {
    transform: translateY(-4px);
  }
  /* bordered cards also brighten their edge toward juice-green on hover. */
  .sw-vs-card:hover,
  .pillars .card:hover {
    border-color: color-mix(in oklab, var(--spot) 60%, var(--hairline-soft));
  }
}

/* number roll-up: tabular digits, width reserved so the rolling figure never
   reflows the words around it (the real value still lives in the HTML). */
.num[data-roll] { display: inline-block; text-align: right; font-variant-numeric: tabular-nums; }

/* magnetic CTA: smooth lean toward the cursor + spring back on leave. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .btn[data-magnetic] {
    transition: transform 0.22s var(--ease), filter var(--dur) var(--ease), background-color var(--dur) var(--ease);
    will-change: transform;
  }
}

/* --------------------------------------------------------------------------
   SHADER FIELD — the signature WebGL iso block-field (built by buildShaders in
   scenes.js on any [data-shader] container). Three framings, all driven by the
   SAME draw; only the mask/scrim changes. A static iso-grid fallback sits behind
   the canvas, so no-WebGL / no-JS just shows the grid. Reduced-motion renders
   one still frame. Used sparingly: the hero, the two dark bands, one divider.
   -------------------------------------------------------------------------- */
.fx-shader { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.scene .fx-shader { z-index: 0; }                 /* behind .scene content (which is z-index 1) */
.fx-shader > canvas,
.fx-band > canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.fx-fallback { position: absolute; inset: 0; background: var(--iso-bg); -webkit-mask: var(--iso-mask); mask: var(--iso-mask); opacity: 0.85; }

/* hero copy sits over the field, left-anchored and measure-constrained. */
.hero-copy { position: relative; max-width: 40rem; }

/* A — full-bleed field + directional legibility scrim (ink on the text side). */
.fx-hero .fx-scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(100deg, var(--cinema-dark) 3%, rgba(23, 24, 27, 0.82) 36%, rgba(23, 24, 27, 0.30) 64%, transparent 84%);
}
@media (max-width: 860px) {
  .fx-hero .fx-scrim { background: linear-gradient(180deg, rgba(23, 24, 27, 0.45), rgba(23, 24, 27, 0.84) 74%); }
}

/* B — border glow / inverted vignette: field only at the periphery, the centre
   stays clean cinema-dark so text reads on flat ground. */
.fx-glow > canvas {
  -webkit-mask: radial-gradient(125% 125% at 50% 50%, transparent 40%, #000 86%);
          mask: radial-gradient(125% 125% at 50% 50%, transparent 40%, #000 86%);
}
.fx-glow .fx-fallback {
  background: radial-gradient(125% 125% at 50% 50%, transparent 54%, rgba(170, 205, 88, 0.06) 92%);
  -webkit-mask: none; mask: none;
}

/* D — thin divider band: a sliver of the field between sections, edges faded,
   a small juice-green block dinkus centred (the "blocks" motif as an ornament). */
.fx-band {
  position: relative; overflow: hidden;
  height: clamp(70px, 8.5vw, 100px);
  margin-block: clamp(var(--space-6), 5vw, var(--space-8));
  border-radius: var(--radius);
  background: var(--cinema-dark);
  border: 1px solid var(--hairline-soft);
}
.fx-band > canvas {
  -webkit-mask: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
          mask: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
}
.fx-band .fx-fallback {
  background: var(--iso-bg);
  -webkit-mask: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
          mask: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
}
/* a centred darkening keeps the callout legible over the live field. */
.fx-band::after { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(58% 130% at 50% 50%, rgba(23, 24, 27, 0.62), transparent 72%); }
.fx-band-callout { position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; justify-content: center; gap: 11px;
  font-size: var(--fs-lead); font-weight: var(--fw-head); letter-spacing: -0.01em; color: var(--rind-cream); padding-inline: var(--space-5); text-align: center; }
.fx-band-callout i { width: 9px; height: 9px; border-radius: 2px; background: var(--juice-green); flex: none; }

/* ==========================================================================
   CENTERED LAYOUT — the chosen direction, applied to every page via this one
   shared layer. One centred axis: the section eyebrows and the left spine drop
   away; headings, hero, intros, figures and demo blocks centre. Dense and
   structured content (tables, code, lists, source notes, content cards, the
   docs steps) stays left-aligned so it keeps reading well.
   ========================================================================== */
/* drop the section eyebrows (kickers) — whether they sit inside a .section-head
   or directly in the container (some heroes), plus the homepage hero label */
.scene .section-head .label,
.scene > .container > .label,
.hero-copy > .label { display: none; }
/* the left spine fights a centred axis — the top progress rail stays */
.spine { display: none; }

/* hero, centred; the shader scrim re-aimed to the centre for legibility */
.hero-copy { max-width: 54rem; margin-inline: auto; text-align: center; }
/* hero prose with its own readability max-width (e.g. .lead) is narrower than
   the hero-copy box, so it must auto-centre too or it floats flush-left and
   reads as "off-centre" under the full-width headline. */
.hero-copy p { margin-inline: auto; }
.fx-hero .fx-scrim {
  background: radial-gradient(125% 95% at 50% 50%, rgba(23, 24, 27, 0.74), rgba(23, 24, 27, 0.35) 68%, transparent 100%);
}

/* section heads + top-level intro prose, centred on one axis */
.scene .section-head { margin-inline: auto; text-align: center; }
.scene > .container > p { margin-inline: auto; text-align: center; max-width: 54rem; }
/* bare headings that sit directly in the container (e.g. compare's hero h1,
   which isn't wrapped in a .section-head) — centre them too. Docs steps and
   blog body headings are nested deeper, so they're unaffected. */
.scene > .container > h1,
.scene > .container > h2,
.scene > .container > h3 { text-align: center; margin-inline: auto; }

/* centre the block elements as blocks (their internals keep their own layout) */
.scene .bp, .scene .widget-ctl, .scene .sw, .scene .sw-vs, .scene .finder,
.scene .mb, .scene .pillars, .scene .pv, .scene .fm-caps, .scene .fm-path-out,
.scene .mock-note, .scene > .container > figure, .scene .vendor-grid,
.scene .chart-wrap, .scene .stage, .scene .hero-ctas { margin-inline: auto; }
.scene .widget-ctl, .scene .sw-chips, .scene .seg, .scene .hero-ctas { justify-content: center; }
.scene .pillars .card { text-align: center; }

/* GUARDS — dense / structured content stays left-aligned and readable */
.scene table, .scene thead, .scene tbody, .scene tr, .scene th, .scene td,
.scene pre, .scene code, .scene ol, .scene ul, .scene li, .scene dl,
.scene figcaption, .scene details, .scene .cite,
.scene .vendor-card, .scene .sw-vs-card, .scene .bp-card,
.scene .step, .scene .doc-step, .scene .rw-step { text-align: start; }

/* ==========================================================================
   NAV MARK — the citrus platter, animated site-wide (founder: "animate
   everywhere"). Sizing + spin/seek promoted from per-page inline <style> to
   this shared layer so every page (incl. all blog posts) animates identically.
   Transform-origins match the SVG viewBox: disc spindle (502.62, 505.01),
   juicer-arm hub (185.6, 821.72). Off under reduced-motion. Pre-existing posts
   that still carry the inline copy are harmlessly overridden by identical values.
   ========================================================================== */
.nv-mark { width: 30px; height: 30px; display: block; }
.nv-disc { transform-box: view-box; transform-origin: 502.62px 505.01px; }
.nv-arm  { transform-box: view-box; transform-origin: 185.6px 821.72px; }
@media (prefers-reduced-motion: no-preference) {
  .nv-disc { animation: nv-spin 26s linear infinite; }
  .nv-arm  { animation: nv-seek 9s ease-in-out infinite; }
}
@keyframes nv-spin { to { transform: rotate(360deg); } }
@keyframes nv-seek {
  0%, 12%  { transform: rotate(0deg); }
  24%, 38% { transform: rotate(12deg); }
  50%, 62% { transform: rotate(-9deg); }
  74%, 86% { transform: rotate(7.5deg); }
  100%     { transform: rotate(0deg); }
}
