/* Patois homepage — implemented from the Figma "Page" frame (1280 x 2751).
   Fixed sizes are the Figma values; everything scales down proportionally below
   1280px and restacks under 780px. */

:root {
  color-scheme: light;

  --bg: #f2ede4;
  --ink: #000;
  --surface: #fff;      /* download buttons          */
  --block: #afafaf;     /* video / demo placeholders */
  --block-alt: #ababab; /* left link card            */
  --swatch: #d9d9d9;    /* artwork placeholder       */

  /* Both embeds have a hard minimum size before they collapse to a compact bar
     (Spotify needs 340x352), so they render at full size and the tile is scaled
     down around them. .626 lands on the 213px square from the design. */
  --tile-scale: .626;
  --tile-w: calc(340px * var(--tile-scale));
  --tile-h: calc(352px * var(--tile-scale));

  /* One gutter, both sides. The Figma sketch insets the left 139px and the right
     only 55px; matched literally that reads as a mistake rather than a choice. */
  --gutter: 10.86%;     /* 139 / 1280 */

  /* Black at .35 over the cream is 2.41:1 - below the 4.5 AA needs for text and
     the 3.0 it needs for a control boundary. These clear both. */
  --muted: rgba(0, 0, 0, .58);   /* 5.2:1 */
  --rule:  rgba(0, 0, 0, .45);   /* 3.3:1 */
  --focus: #1c53d6;
  --accent: #e8432e;

  --fs-brand: clamp(16px, 1.5625vw, 20px);
  --fs-h1: clamp(26px, 3.6vw, 46px);
  --fs-cta: clamp(18px, 1.875vw, 24px);
  --fs-body: clamp(15px, 1.5625vw, 20px);
  --fs-small: clamp(13px, 1.25vw, 16px);
}

* { box-sizing: border-box; }

/* Nothing on this page had a visible focus state, and the input actively removed
   one. Every control gets the same ring. */
:where(a, button, input, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 4px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Host Grotesk", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 400;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; }

a { color: inherit; }

.page {
  position: relative;
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
}

/* ---------- scattered artwork ---------- */

.art {
  --tx: 0px;
  --ty: 0px;
  position: absolute;
  z-index: 0;
  pointer-events: none;
  object-fit: cover;
}
.art--blue   { left: 0;      top: 113px; width: 9.14%;  aspect-ratio: 1; } /* 117 */
.art--spiral { left: 3.2%;   top: 320px; width: 12.03%; aspect-ratio: 1; } /* 154 */
.art--leave  { left: 76.48%; top: 169px; width: 9.06%;  aspect-ratio: 1; } /* 116 */
.art--flower { left: 82.42%; top: 337px; width: 12.42%; aspect-ratio: 1; } /* 159 */

/* ---------- masthead ---------- */

.masthead {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 26px var(--gutter) 0;
}

.brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;          /* was a 27px tap target */
  font-size: var(--fs-brand);
  font-weight: 700;
  text-decoration: none;
}

/* ---------- download button ---------- */

/* The design draws this as a plain white rectangle, standing in for the App
   Store badge. Square-edged pure white punches a hole in the cream, so it gets
   a radius and a soft edge to sit on the background instead. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 10px 16px;
  border-radius: 10px;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(38, 30, 18, .10), 0 6px 18px rgba(38, 30, 18, .06);
  font-size: var(--fs-cta);
  text-decoration: none;
  white-space: nowrap;
  transition: background .2s ease, color .2s ease, box-shadow .2s ease;
}
.btn:hover {
  background: var(--ink);
  color: var(--surface);
  box-shadow: 0 2px 4px rgba(38, 30, 18, .16), 0 10px 26px rgba(38, 30, 18, .12);
}
.btn:hover .btn__logo { filter: invert(1); }

.btn__logo {
  width: calc(var(--fs-cta) * 1.108);   /* 26.584 / 24 */
  height: calc(var(--fs-cta) * 1.362);  /* 32.685 / 24 */
  flex: none;
}

/* ---------- headline ---------- */

.headline {
  position: relative;
  z-index: 1;
  margin: 55px 0 0;
  padding: 0 var(--gutter);
  font-size: var(--fs-h1);
  font-weight: 700;
  line-height: normal;
}

/* ---------- spotify -> apple music ---------- */

.convert {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4%;
  margin-top: 38px;
  perspective: 1100px;          /* the stage the players float in */
}

.card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: var(--tile-w);
  margin: 0;
}

/* One square tile per service, each holding a full-size embed scaled to fit. */
.card__embed {
  position: relative;
  overflow: hidden;
  width: var(--tile-w);
  height: var(--tile-h);
  border-radius: 12px;
}

.card__stage {
  position: absolute;
  top: 0;
  left: 0;
  width: 340px;
  height: 352px;
  transform: scale(var(--tile-scale));
  transform-origin: top left;
}

.card__embed iframe {
  display: block;
  width: 100%;
  border: 0;
  background: transparent;
}

.card--spotify .card__embed iframe { height: 100%; }

/* Both tiles rest as a plain album cover, like every other image on the page,
   and hand over to the real player on hover. Spotify's embed fills the tile, so
   its cover sits on top and fades; Apple's is a fixed bar, so it slides up over
   the cover instead. */
.card__cover,
.card--apple .card__art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.card__cover {
  z-index: 2;
  pointer-events: none;          /* play is clickable straight through */
  opacity: 1;
  transition: opacity .3s ease;
}
.card:hover .card__cover,
.card:focus-within .card__cover { opacity: 0; }

.card--apple .card__embed iframe {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: 175px;
  opacity: 0;
  transform: translateY(12%);
  transition: opacity .3s ease, transform .3s ease;
}
.card--apple:hover .card__embed iframe,
.card--apple:focus-within .card__embed iframe {
  opacity: 1;
  transform: translateY(0);
}

/* No hover to give: show the players outright. The cover stays put behind them
   rather than being removed - if an embed is slow or refuses to render, the tile
   falls back to the album art instead of an empty box. */
@media (hover: none) {
  .card__cover { z-index: 0; }
  .card--spotify .card__embed iframe { position: relative; z-index: 1; }
  .card--apple .card__embed iframe { opacity: 1; transform: none; }
}

.card figcaption {
  font-size: var(--fs-small);
  line-height: normal;
}

.convert__arrow {
  width: 4.24%;                 /* 54 / 1280, the rotated bounding box */
  min-width: 30px;
  max-width: 54px;
  flex: none;
}
/* The float handler only writes --rx/--ry/--tx/--ty/--lift; the easing below is
   what actually animates, so there is no JS animation loop to stall or die.
   Any [data-float] element joins in - depth and direction come from the markup. */
[data-float] {
  --rx: 0deg;
  --ry: 0deg;
  --tx: 0px;
  --ty: 0px;
  --lift: 0;
  transform:
    translate3d(var(--tx), var(--ty), 0)
    rotateX(var(--rx))
    rotateY(var(--ry));
  transform-style: preserve-3d;
  transition: transform var(--float-ms, 420ms) var(--float-ease, cubic-bezier(.22, 1.24, .36, 1));
}

.convert__arrow[data-float] { transform: translate3d(var(--tx), var(--ty), 0) rotate(137.8deg); }

.card__embed {
  box-shadow:
    0 calc(6px + var(--lift) * 16px) calc(14px + var(--lift) * 30px)
    rgba(38, 30, 18, calc(.10 + var(--lift) * .16));
  transition: box-shadow var(--float-ms, 420ms) ease-out;
}

/* Anything at background depth sits further away, so it lags further behind. */
.art,
.headline[data-float],
.step__label[data-float] {
  transition: transform calc(var(--float-ms, 700ms) * 1.9) cubic-bezier(.2, .7, .3, 1);
}

.is-floating [data-float] { will-change: transform; }

/* Measuring resets the transforms; never animate that. */
.is-measuring [data-float],
.is-measuring .card__embed,
.is-measuring .step__media { transition: none !important; }

/* ---------- get the app ---------- */

.get {
  position: relative;
  z-index: 1;
  margin-top: 88px;
  text-align: center;
}

.btn--lg { padding: 12px 20px; }

.paste {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 18px;
}

.paste__label,
.paste__out { flex-basis: 100%; }

.paste__label {
  font-size: var(--fs-small);
  line-height: normal;
}

.paste__input {
  width: min(360px, 62%);
  min-height: 44px;                     /* was a 34px target */
  padding: 6px 2px;
  border: 0;
  border-bottom: 1px solid var(--rule); /* .35 alpha was 2.41:1, under the 3.0 AA */
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: var(--fs-small);
  text-align: center;
}
.paste__input::placeholder { color: var(--muted); }
.paste__input:focus { border-bottom-color: var(--ink); }

.paste__go {
  min-height: 44px;          /* was 35px */
  padding: 0 18px;
  border: 1px solid var(--ink);
  border-radius: 10px;       /* matches the download buttons */
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: var(--fs-small);
  cursor: pointer;
}
.paste__go:hover { background: var(--ink); color: var(--bg); }
.paste__go[disabled] { opacity: .45; cursor: default; }

.paste__out {
  min-height: 1.4em;
  margin: 2px 0 0;
  font-size: var(--fs-small);
  line-height: normal;
}
.paste__out--error { color: #9b1c1c; }

/* ---------- story ---------- */

.story {
  position: relative;
  z-index: 1;
  margin-top: 26px;
  text-align: center;
}

.story p {
  margin: 0;
  font-size: var(--fs-body);
  line-height: 42px;
}

/* ---------- steps ---------- */

.steps { position: relative; z-index: 1; margin-top: 107px; }

.step + .step { margin-top: 128px; }

.step__label {
  margin: 0;
  font-size: var(--fs-body);
  line-height: normal;
}

.step__media {
  position: relative;
  overflow: hidden;
  border-radius: 17px;
  background: var(--block);
  box-shadow:
    0 calc(4px + var(--lift, 0) * 18px) calc(12px + var(--lift, 0) * 34px)
    rgba(38, 30, 18, calc(.06 + var(--lift, 0) * .14));
  transition: box-shadow var(--float-ms, 700ms) ease-out;
}
.step__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* All three steps share the page gutter, so the media blocks land on the same
   two edges the masthead, headline and footer use. The Figma sketch starts them
   at 97 / 655 / 181 - three different edges that read as drift, not rhythm.
   The left/right/full alternation is kept; only the edges are snapped. */
.steps { padding: 0 var(--gutter); }

.step--left,
.step--right { display: grid; align-items: center; gap: 3.5%; }

.step--left  { grid-template-columns: 38.85% 1fr; }   /* media | label */
.step--right { grid-template-columns: 1fr 38.85%; }   /* label | media */

.step--left .step__media,
.step--right .step__media { aspect-ratio: 385 / 298; }

.step--left .step__media { grid-column: 1; }
.step--left .step__label { grid-column: 2; }
.step--right .step__label { grid-column: 1; }
.step--right .step__media { grid-column: 2; }

.step--wide { text-align: center; }
.step--wide .step__media {
  width: 100%;
  margin: 0;
  aspect-ratio: 889 / 298;
}

.step__label { text-wrap: balance; }

.step--wide .step__label { margin-bottom: 29px; }

/* ---------- step demos (animated stand-ins until real clips are dropped in) ----------
   Each scene is sized from the block's height (cqh) so it scales with the layout.
   The base styles are the finished frame; the keyframes only build up to it, so
   reduced-motion (animation: none) lands on a complete picture. */

:root { --stage: #e7dfd0; --demo-t: 7s; }

.step__media--demo {
  background: var(--stage);
  container-type: size;
  container-name: demo;
}
.step__media video { position: absolute; inset: 0; }   /* a real clip covers the mockup */

.demo {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 5.8cqh;
  line-height: 1.25;
  color: #111;
  animation: demo-life var(--demo-t) ease-in-out infinite both;
}
:where(.demo p, .demo b, .demo i, .demo span) { margin: 0; font-style: normal; }
.demo img { display: block; }

.demo-js .demo, .demo-js .demo * { animation-play-state: paused; }
.demo-js .is-live .demo, .demo-js .is-live .demo * { animation-play-state: running; }

@keyframes demo-life { 0% { opacity: 0; } 5%, 93% { opacity: 1; } 100% { opacity: 0; } }

/* -- 1. the message -- */
.demo--msg { flex-direction: column; align-items: flex-start; justify-content: center; gap: 4cqh; padding: 0 10cqh; }
.demo__time { align-self: center; font-size: .8em; color: rgba(0,0,0,.5); }
.bubble { border-radius: 4.4cqh; background: #fff; box-shadow: 0 .6cqh 2cqh rgba(38,30,18,.08); }
.bubble--link { display: flex; align-items: center; gap: 3.4cqh; padding: 3cqh; border-bottom-left-radius: 1.4cqh; animation: msg-in var(--demo-t) cubic-bezier(.2,.9,.3,1.2) infinite both; }
.bubble--link img { width: 26cqh; height: 26cqh; border-radius: 2cqh; object-fit: cover; }
.bubble__meta { display: grid; gap: .4cqh; padding-right: 3cqh; }
.bubble__meta b { font-size: 1.05em; }
.bubble__meta span { color: rgba(0,0,0,.62); }
.bubble__meta i { color: rgba(0,0,0,.45); font-size: .85em; }
.bubble--text { padding: 2.4cqh 4cqh; border-bottom-left-radius: 1.2cqh; animation: msg-in-late var(--demo-t) cubic-bezier(.2,.9,.3,1.2) infinite both; }
@keyframes msg-in      { 0%, 8% { opacity: 0; transform: translateY(5cqh) scale(.94); } 18%, 100% { opacity: 1; transform: none; } }
@keyframes msg-in-late { 0%, 26% { opacity: 0; transform: translateY(5cqh) scale(.94); } 36%, 100% { opacity: 1; transform: none; } }

/* -- 2. the share sheet -- */
.demo--share { align-items: flex-end; }
.sheet { width: 88%; padding: 3.4cqh 3.4cqh 4cqh; border-radius: 4.4cqh 4.4cqh 0 0; background: rgba(255,255,255,.92); box-shadow: 0 -1cqh 4cqh rgba(38,30,18,.12); animation: sheet-up var(--demo-t) cubic-bezier(.2,.8,.2,1) infinite both; }
.sheet__head { display: flex; align-items: center; gap: 2.8cqh; padding-bottom: 3cqh; margin-bottom: 3cqh; border-bottom: .3cqh solid rgba(0,0,0,.1); }
.sheet__head img { width: 14cqh; height: 14cqh; border-radius: 1.6cqh; object-fit: cover; }
.sheet__head p { display: grid; }
.sheet__head span { font-size: .85em; color: rgba(0,0,0,.5); }
.sheet__apps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2cqh; text-align: center; font-size: .82em; }
.app { position: relative; display: grid; justify-items: center; gap: 1.2cqh; }
.app__ico { width: 18cqh; height: 18cqh; border-radius: 4cqh; background: #d6d0c4; object-fit: cover; }
.app--patois .app__ico { animation: icon-press var(--demo-t) ease-in-out infinite both; }
.app--patois span { font-weight: 700; }
.tap { position: absolute; justify-self: start; top: 3.5cqh; left: 50%; width: 13cqh; height: 13cqh; margin-left: -6.5cqh; border-radius: 50%; border: .6cqh solid rgba(0,0,0,.55); background: rgba(0,0,0,.08); opacity: 0; animation: tap-ring var(--demo-t) ease-out infinite both; }
@keyframes sheet-up   { 0%, 8% { transform: translateY(105%); } 24%, 100% { transform: none; } }
@keyframes icon-press { 0%, 46% { transform: none; } 52% { transform: scale(.88); } 60% { transform: scale(1.08); } 68%, 100% { transform: none; } }
@keyframes tap-ring   { 0%, 44% { opacity: 0; transform: scale(.5); } 50% { opacity: 1; transform: scale(1); } 64%, 100% { opacity: 0; transform: scale(1.7); } }

/* -- 3. the result -- */
.demo--result { gap: 10cqh; }
.res { position: relative; display: grid; justify-items: center; gap: 1.6cqh; }
.res img { width: 56cqh; height: 56cqh; border-radius: 3cqh; object-fit: cover; box-shadow: 0 1cqh 3cqh rgba(38,30,18,.18); }
.res span { font-size: .85em; color: rgba(0,0,0,.6); }
.res--from { animation: res-from var(--demo-t) ease-out infinite both; }
.res--to   { animation: res-to var(--demo-t) cubic-bezier(.2,.9,.3,1.25) infinite both; }
.res__arrow { width: 18cqh; height: auto; color: #111; stroke-dasharray: 1; stroke-dashoffset: 0; animation: arrow-draw var(--demo-t) ease-in-out infinite both; }
.res__pill { position: absolute; bottom: -12cqh; padding: 1.6cqh 3.6cqh; border-radius: 99cqh; background: #fff; font-size: .78em; font-weight: 400; white-space: nowrap; box-shadow: 0 .6cqh 2cqh rgba(38,30,18,.1); animation: pill-in var(--demo-t) ease-out infinite both; }
@keyframes res-from   { 0%, 6% { opacity: 0; transform: translateX(-3cqh); } 16%, 100% { opacity: 1; transform: none; } }
@keyframes arrow-draw { 0%, 30% { stroke-dashoffset: 1; } 44%, 100% { stroke-dashoffset: 0; } }
@keyframes res-to     { 0%, 44% { opacity: 0; transform: scale(.7); } 56%, 100% { opacity: 1; transform: none; } }
@keyframes pill-in    { 0%, 60% { opacity: 0; transform: translateY(2cqh); } 70%, 100% { opacity: 1; transform: none; } }

/* Narrow blocks (phones): stack the result vertically and shrink the covers. */
@container demo (aspect-ratio < 1.6) {
  .demo--result { flex-direction: column; gap: 2.4cqh; }
  .res img { width: 30cqh; height: 30cqh; }
  .res__arrow { width: 11cqh; transform: rotate(90deg); }
  .res__pill { position: static; margin-top: .4cqh; }
  .res--to { gap: .8cqh; }
}

@media (prefers-reduced-motion: reduce) {
  .demo, .demo *, .demo-js .demo, .demo-js .demo * { animation: none !important; }
}

/* ---------- footer ---------- */

.footer { margin-top: 199px; }

.footer__stage {
  position: relative;
  height: 24.45vw;                      /* 313 / 1280 */
  max-height: 313px;
  overflow: hidden;
}

.footer__radio {
  position: absolute;
  z-index: 1;            /* the radio sits in front of the wordmark */
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 33.52%;                        /* 429 / 1280 */
}

/* Six copies scrolling by exactly three of them: the track lands on an identical
   frame every cycle, so the loop has no visible seam. */
.footer__wordmark {
  position: absolute;
  top: 25.24%;                          /*  79 / 313 of the stage height */
  left: 0;
  display: flex;
  width: max-content;
  margin: 0;
  font-size: min(20.77vw, 265.794px);   /* 265.794 / 1280 */
  line-height: 1.332;
  white-space: nowrap;
  will-change: transform;
  animation: wordmark-scroll var(--marquee-s, 30s) linear infinite;
}

@keyframes wordmark-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

.footer__legal {
  margin: 0;
  padding: 22px var(--gutter) 18px;
  font-size: var(--fs-small);
  line-height: 1;
}
/* 21px links were the smallest targets on the page */
.footer__legal a {
  display: inline-block;
  padding: 14px 3px;      /* 16px text + 28 = 44 */
}

/* ---------- narrow screens ---------- */

@media (max-width: 780px) {
  :root {
    --gutter: 22px;
    --fs-h1: 30px;
    --fs-cta: 17px;
    --fs-body: 16px;
    --fs-small: 14px;
    --fs-brand: 16px;
    --tile-scale: .8;
  }

  /* The scattered artwork is desktop decoration. At this width it sits flush to
     both edges, reads as broken bleed, and pushes the headline down the page. */
  .art { display: none; }

  .headline { margin-top: 34px; }

  .convert {
    flex-direction: column;
    align-items: center;
    gap: 12px;
    margin-top: 28px;
    padding: 0 var(--gutter);
    perspective: none;
  }
  .card { width: var(--tile-w); }

  /* Must out-specify .convert__arrow[data-float], or it keeps pointing right
     across a stack that now runs top to bottom. */
  .convert__arrow[data-float] {
    width: 26px;
    min-width: 26px;
    transform: rotate(227.8deg);
  }

  .get { margin-top: 40px; padding: 0 var(--gutter); }
  .paste { gap: 8px 10px; margin-top: 14px; }
  .paste__input { flex: 1 1 auto; width: auto; min-width: 0; }
  .paste__go { flex: none; }

  /* One line per thought. The desktop 42px leading is taller than the type, so
     once these sentences wrap you cannot tell a wrap from a new line. */
  .story { margin-top: 44px; padding: 0 var(--gutter); }
  .story p { line-height: 1.4; }
  .story p + p { margin-top: 12px; }

  .steps { margin-top: 52px; padding: 0 var(--gutter); }
  .step + .step { margin-top: 44px; }

  .step--left,
  .step--right {
    grid-template-columns: 1fr;
    gap: 10px;
  }
  .step--left .step__media,
  .step--right .step__media { grid-column: 1; }
  .step--left .step__label,
  .step--right .step__label { grid-column: 1; grid-row: 1; margin-top: 0; }
  .step--wide .step__label { margin-bottom: 10px; }
  .step--wide .step__media { width: 100%; aspect-ratio: 385 / 298; }

  .footer { margin-top: 56px; }
  .footer__stage { height: 46vw; }
  .footer__radio { width: 56%; }
  .footer__wordmark { top: 12%; font-size: 22vw; }
  .footer__legal { padding-top: 20px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }

  .footer__wordmark { animation: none; transform: translateX(-8%); }

  [data-float] {
    --tx: 0px !important;
    --ty: 0px !important;
    --rx: 0deg !important;
    --ry: 0deg !important;
    --lift: 0 !important;
  }
}

/* ---------- float tuner (only rendered on #tune) ---------- */

.tuner {
  position: fixed;
  z-index: 99;
  right: 16px;
  bottom: 16px;
  width: 232px;
  padding: 14px;
  border: 1px solid rgba(0, 0, 0, .18);
  border-radius: 10px;
  background: rgba(255, 255, 255, .94);
  backdrop-filter: blur(8px);
  font-size: 12px;
  line-height: 1.4;
}
.tuner h2 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 700;
}
.tuner__hide {
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.tuner__hide:hover { background: rgba(0, 0, 0, .08); }
.tuner label {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 8px;
  margin-bottom: 9px;
}
.tuner output { font-variant-numeric: tabular-nums; opacity: .6; }
.tuner input { grid-column: 1 / -1; width: 100%; margin: 0; }
.tuner button {
  width: 100%;
  margin-top: 4px;
  padding: 6px;
  border: 1px solid var(--ink);
  border-radius: 6px;
  background: transparent;
  font: inherit;
  cursor: pointer;
}
.tuner button:hover { background: var(--ink); color: #fff; }

/* ---------- a little mischief ---------- */

/* a wobbly underline on the punchline, drawn by the browser */
.squiggle {
  white-space: nowrap;
  text-decoration: underline wavy var(--accent);
  text-decoration-thickness: .07em;
  text-underline-offset: .16em;
  text-decoration-skip-ink: none;
}

/* the artwork keeps moving, even when nobody is touching the mouse */
.art--spiral { animation: art-spin 40s linear infinite; }
.art--flower { animation: art-sway 6s ease-in-out infinite; }
.art--leave  { animation: art-sway 8s ease-in-out -3s infinite; }
@keyframes art-spin { to { rotate: 360deg; } }
@keyframes art-sway { 0%, 100% { rotate: -7deg; } 50% { rotate: 7deg; } }

/* the two players sit like pinned-up polaroids; the transform stays free for
   the cursor float, so the tilt rides on the standalone rotate property */
.card--spotify { rotate: -3.5deg; }
.card--apple   { rotate: 3deg; }
.card { transition: rotate .3s cubic-bezier(.3, 1.6, .5, 1); }
.card:hover { rotate: 0deg; }

.convert__arrow { animation: arrow-nudge 1.6s ease-in-out infinite; }
@keyframes arrow-nudge { 0%, 100% { translate: 0 0; } 50% { translate: 8px 0; } }

.step--left  .step__media { rotate: -1.6deg; }
.step--right .step__media { rotate: 1.6deg; }
.step--wide  .step__media { rotate: -.6deg; }

.btn:hover { rotate: -2deg; }

@media (max-width: 780px) {
  .convert__arrow { animation: none; }
  .card--spotify, .card--apple { rotate: 0deg; }
}
@media (prefers-reduced-motion: reduce) {
  .card, .btn { rotate: none; }
}
