/* ===========================================================================
   Design system.

   Transcribed from the supplied Vinyl specification and generalised so the
   whole product is built from it, not just the hero: the sky palette, the
   Instrument Serif / Instrument Sans stack, the liquid
   button, the staggered entrance and the clip-path menu wipe.

   app.css builds the application chrome from these same tokens, so a screen
   deep inside the product reads as the same site as the landing page.
   =========================================================================== */

:root{
  --ink:#0b0b0c;
  --sky:#dbe6ee;
  --paper:#e9f0f6;

  --muted:rgba(11,11,12,.56);
  --quiet:rgba(11,11,12,.40);
  --line:rgba(11,11,12,.16);
  --line-soft:rgba(11,11,12,.09);
  --wash:rgba(255,255,255,.55);

  --display:"Instrument Serif",Georgia,serif;
  --ui:"Instrument Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;

  --nav-gap:clamp(1.1rem, 2.2vw, 2.15rem);
  --pad-x:clamp(1.25rem, 4vw, 3.6rem);
  --ease:cubic-bezier(.22,1,.36,1);
  --ease-in-out:cubic-bezier(.76,0,.24,1);

  --poster:url("https://images.higgs.ai/?default=1&output=webp&url=https%3A%2F%2Fd8j0ntlcm91z4.cloudfront.net%2Fuser_38xzZboKViGWJOttwIXH07lWA1P%2Fhf_20260908_122011_59c97465-4d23-4fdc-ac40-f6832f573e28.png&w=1920&q=85");
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}

body{
  margin:0;
  color:var(--ink);
  background:var(--sky);
  overflow-x:hidden;
  font-family:var(--ui);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
}

a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
body.menu-open{overflow:hidden}

/* ---------------------------------------------------------------- hero -- */

/* The scroll rig.

   The section is tall; the stage inside it is sticky. Scrolling the section
   does not move the stage — it drives the camera. The height IS the length
   of the move, so lengthen it to slow the dive down. */
.hero-scroll{
  position:relative;
  height:calc(100svh + 2200px);
}

.hero{
  position:sticky;
  top:0;
  height:100svh;
  min-height:100svh;
  overflow:hidden;
  display:flex;
  flex-direction:column;
  background:var(--sky);
  isolation:isolate;
}

/* The camera move. Everything in the plate scales and turns together about
   the record's centre — see ui/vinyl.js for why the rotation is eased the
   way it is.

   It only applies once `is-settled` is on, because the entrance animation
   below owns `transform` until it finishes and `animation-fill-mode:forwards`
   means a finished animation keeps holding the property. An animated value
   beats a normal declaration, so without the handover the camera computes
   perfectly and paints nothing. */
.hero__media.is-settled{
  animation:none;
  opacity:1;
  filter:none;
  /* Two class selectors, so this wins over the base .hero__media rule's
     own `transform: scale(1.08)` further down the file no matter which
     order they end up in. */
  transform:
    translate3d(var(--hero-mx, 0px), var(--hero-my, 0px), 0)
    scale(var(--hero-zoom, 1));
  transform-origin:var(--hero-origin-x, 48%) var(--hero-origin-y, 61.3%);
  opacity:var(--hero-media-opacity, 1);
}

/* The filmed plate no longer rotates.
   Turning a photograph of a meadow is a photograph of a meadow turning — the
   horizon tilts and the grass goes round with it. Only the record should
   turn, so only the drawn record does. */

/* ------------------------------------------------------- drawn record -- */
/* Laid out big and scaled down, never the other way round for most of the
   move: a 2400px disc drawn at 0.2 is exact, the same disc drawn at 200px and
   blown up to 2400 is the mush this replaced. */

.hero__record{
  --record-base:min(210vmin, 2200px);
  position:absolute;
  /* Centred on the stage, not on the filmed record: by the time it is
     visible the meadow has gone and there is nothing left to line up with. */
  left:50%;
  top:50%;
  z-index:8;
  width:var(--record-base);
  height:var(--record-base);
  margin-left:calc(var(--record-base) / -2);
  margin-top:calc(var(--record-base) / -2);
  pointer-events:none;
  opacity:var(--record-opacity, 0);
  transform:
    translate3d(var(--hero-mx, 0px), var(--hero-my, 0px), 0)
    scale(var(--record-scale, .15));
}

.hero__record-disc{
  position:absolute;
  inset:0;
  border-radius:50%;
  transform:rotate(var(--record-spin, 0deg));
  background:
    radial-gradient(circle at 50% 50%,
      rgba(255,255,255,.055) 0 16.5%, rgba(0,0,0,0) 17% 100%),
    conic-gradient(from 210deg,
      #0a0a0c 0deg, #2c2c35 42deg, #0b0b0e 94deg, #373742 152deg,
      #0a0a0c 204deg, #27272f 266deg, #0c0c10 318deg, #0a0a0c 360deg);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.07);
}

/* The grooves, on their own layer and faded in with size.

   Spacing is in percentages so it stays in proportion as the disc grows. At a
   third of full size that spacing lands under a pixel and the whole face
   moires into a smudge — worse than no detail at all — so the layer only
   arrives once the disc is big enough for grooves to read as grooves. */
.hero__record-disc::after{
  content:"";
  position:absolute;
  inset:0;
  border-radius:50%;
  opacity:var(--record-groove, 0);
  /* Coarse on purpose. Finer grooves are more accurate and look worse: the
     disc is painted at its layout size and then scaled, and at a period of
     about ten pixels the downsample turned the whole face into wavy moiré
     arcs that read as a rendering fault. Roughly twenty pixels is clear of
     it at every size this disc is ever shown. */
  background:repeating-radial-gradient(circle at 50% 50%,
    rgba(255,255,255,.055) 0 .17%, rgba(0,0,0,0) .17% 1.15%);
  /* Nothing inside the label: a record's grooves stop at the paper. */
  -webkit-mask-image:radial-gradient(circle at 50% 50%,
    transparent 0 19%, #000 20.5% 100%);
  mask-image:radial-gradient(circle at 50% 50%,
    transparent 0 19%, #000 20.5% 100%);
}

.hero__record-label{
  position:absolute;
  inset:38.5%;
  border-radius:50%;
  background:radial-gradient(circle at 42% 34%, #f7eeda, #ddcaa4 58%, #c2ab7b);
  box-shadow:0 0 0 1px rgba(0,0,0,.35);
  color:#2a2113;
  font-family:var(--display);
  font-size:calc(var(--record-base) * .042);
  letter-spacing:-.01em;
}
.hero__record-label > span{
  position:absolute;
  left:0;right:0;
  bottom:17%;
  text-align:center;
}
.hero__record-label::after{
  content:"";
  position:absolute;
  left:50%;top:50%;
  width:7%;
  aspect-ratio:1;
  border-radius:50%;
  transform:translate(-50%,-50%);
  background:#0a0a0c;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.16);
}

/* Light lying across the vinyl. Outside the rotating disc on purpose — a
   highlight that turned with the record would read as a spinning picture of
   a record rather than a record spinning under the sun. */
.hero__record-sheen{
  position:absolute;
  inset:0;
  border-radius:50%;
  mix-blend-mode:screen;
  background:linear-gradient(116deg,
    rgba(255,255,255,0) 26%,
    rgba(255,236,206,.20) 44%,
    rgba(255,255,255,.04) 53%,
    rgba(255,255,255,0) 70%);
}

/* The copy leaves before the push-in really starts. */
.hero__inner,
.nav{
  transform:translate3d(0, var(--hero-copy-y, 0px), 0);
  opacity:var(--hero-copy-opacity, 1);
}
.hero__inner a,
.hero__inner button,
.nav a,
.nav button{
  pointer-events:var(--hero-copy-events, auto);
}

/* Black vinyl closing over the lens. A touch of the groove's sheen off to
   one side so it does not read as a plain fade to black. */
.hero__groove{
  position:absolute;
  inset:0;
  /* Under the record, which is the whole point of it. Both sat at 8 and the
     groove came later in the DOM, so the darkness painted straight over the
     disc and the second half of the dive was a black rectangle. */
  z-index:7;
  pointer-events:none;
  opacity:var(--groove-opacity, 0);
  background:
    radial-gradient(38% 38% at 48% 61.3%, rgba(24,18,16,.55) 0%, rgba(12,10,10,.92) 46%, #0a0908 72%),
    #0a0908;
}

.hero__media{
  position:absolute;
  inset:0;
  z-index:0;
  transform:scale(1.08);
  filter:blur(14px);
  opacity:0;
  animation:mediaIn 1.9s var(--ease) .1s forwards;
}

@keyframes mediaIn{ to{ transform:scale(1); filter:blur(0); opacity:1 } }

/* The blurred same-frame backdrop. Off by default; switched on for portrait
   phones, where the footage is bottom-anchored and no longer covers. */
.hero__media::before{
  content:"";
  display:none;
  position:absolute;
  inset:-8%;
  z-index:0;
  background:var(--poster) center 22%/cover no-repeat;
  filter:blur(30px) saturate(104%);
  transform:scale(1.05);
}

/* Keeps the ink type crisp against the sky without visibly dimming the clip. */
.hero__media::after{
  content:"";
  position:absolute;
  inset:0;
  z-index:3;
  pointer-events:none;
  background:
    linear-gradient(to bottom, rgba(226,236,243,.30) 0%, rgba(226,236,243,.10) 32%, rgba(226,236,243,0) 54%),
    radial-gradient(120% 62% at 50% 0%, rgba(255,255,255,.20), rgba(255,255,255,0) 58%);
}

.hero__media img,
.hero__media video{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:center 62%;
  display:block;
}
/* The breeze.

   It paints nothing itself — backdrop-filter distorts whatever is behind it,
   and the mask decides where. Feathered from nothing at the top of the strip
   to full at 20% down, so there is no seam where the effect begins. Above
   ::after (3) so the wash is not dragged around with the grass. */
.hero__breeze{
  opacity:var(--breeze-opacity, 1);
  visibility:var(--breeze-visibility, visible);
  position:absolute;
  left:0;right:0;bottom:0;
  /* Low enough to clear the turntable. At 54% it reached the plinth and bent
     its straight edges, which reads as a broken render rather than wind. The
     long feather means the effect arrives gradually in the grass instead of
     starting on a line. */
  height:30%;
  z-index:4;
  pointer-events:none;
  backdrop-filter:url(#breeze);
  -webkit-backdrop-filter:url(#breeze);
  -webkit-mask-image:linear-gradient(to bottom, transparent 0%, rgba(0,0,0,.45) 38%, #000 72%);
  mask-image:linear-gradient(to bottom, transparent 0%, rgba(0,0,0,.45) 38%, #000 72%);
}

/* Portrait phones bottom-anchor the footage at 64svh, so the grass sits in a
   different place and the strip has to follow it. */
@media (max-width:900px) and (orientation:portrait){
  .hero__breeze{height:22%}
}

.filter-defs{position:absolute;width:0;height:0;overflow:hidden}

.hero__media img{z-index:1}
.hero__media video{z-index:2;opacity:0;transition:opacity 1.2s ease}
.hero__media video.is-ready{opacity:1}

/* ----------------------------------------------------------------- nav -- */

.nav{
  position:relative;
  z-index:60;                 /* above .menu (50), or the X gets trapped */
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:1rem;
  padding:clamp(1.1rem,2.4vw,1.9rem) var(--pad-x);
}

/* The wordmark is set in the UI face now, not the gothic it started in.
   Tightened and sized down with it: a sans at the old size reads as
   shouting rather than as a mark. */
.logo{
  display:inline-flex;
  align-items:center;
  font-family:var(--ui);
  font-weight:500;
  font-size:clamp(1.25rem,1.9vw,1.55rem);
  line-height:1;
  letter-spacing:-.025em;
  white-space:nowrap;
  color:var(--ink);
}
.logo-suffix{font-family:var(--ui);font-weight:400;font-size:.55em;margin-left:.12em;opacity:.6}
.logo-mark{display:block;width:1.2em;height:auto;margin-right:.4em;flex:none}

.nav__links{
  display:flex;
  gap:var(--nav-gap);
  list-style:none;
  margin:0;
  padding:0;
  position:absolute;
  left:50%;
  top:50%;
  transform:translate(-50%,-50%);
}
.nav__links a{
  display:inline-block;
  position:relative;
  font-size:clamp(.95rem,1.05vw,1.05rem);
  letter-spacing:.005em;
  padding:.15rem 0;
}
.nav__links a::after{
  content:"";
  position:absolute;
  left:0; right:0; bottom:-2px;
  height:1px;
  background:currentColor;
  transform:scaleX(0);
  transform-origin:right;
  transition:transform .5s var(--ease);
}
.nav__links a:hover::after{transform:scaleX(1);transform-origin:left}

.nav__right{display:flex;align-items:center;gap:.6rem}

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

.btn,
.btn-play{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:.5em;
  background:var(--ink);
  color:#fff;
  border-radius:6px;
  padding:.72rem 1.15rem;
  font-size:.95rem;
  line-height:1;
  white-space:nowrap;
  cursor:pointer;
  transition:transform .45s var(--ease), background-color .45s var(--ease),
             box-shadow .45s var(--ease), color .45s var(--ease),
             border-color .45s var(--ease), opacity .3s ease;
}
.btn:hover,
.btn-play:hover{
  transform:translateY(-2px);
  background:#1e1e21;
  box-shadow:0 10px 24px -12px rgba(0,0,0,.6);
}
.btn:active,
.btn-play:active{transform:translateY(0)}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.42;cursor:not-allowed;pointer-events:none}

.btn-ghost{
  background:transparent;
  color:var(--ink);
  border:1px solid var(--line);
}
.btn-ghost:hover{
  background:rgba(255,255,255,.5);
  border-color:rgba(11,11,12,.34);
  box-shadow:0 10px 24px -16px rgba(0,0,0,.35);
}

.btn-quiet{
  background:rgba(255,255,255,.45);
  color:var(--ink);
  border:1px solid var(--line-soft);
}
.btn-quiet:hover{background:rgba(255,255,255,.75);border-color:var(--line)}

.btn-block{width:100%}
.btn-sm{padding:.55rem .85rem;font-size:.86rem}

/* ------------------------------------------------------------- hamburger -- */

.burger{
  display:none;
  position:relative;
  z-index:60;
  width:44px;
  height:44px;
  margin-right:-8px;
  border-radius:8px;
  flex:0 0 auto;
}
.burger span{
  position:absolute;
  left:11px;
  width:22px;
  height:1.6px;
  background:var(--ink);
  border-radius:2px;
  transition:transform .55s var(--ease-in-out), opacity .3s ease, width .55s var(--ease-in-out);
}
.burger span:nth-child(1){top:17px}
.burger span:nth-child(2){top:22px;width:15px}
.burger span:nth-child(3){top:27px}
body.menu-open .burger span:nth-child(1),
.burger.is-active span:nth-child(1){transform:translateY(5px) rotate(45deg)}
body.menu-open .burger span:nth-child(2),
.burger.is-active span:nth-child(2){opacity:0;transform:translateX(10px)}
body.menu-open .burger span:nth-child(3),
.burger.is-active span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}

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

.hero__inner{
  position:relative;
  z-index:10;
  flex:1;
  display:flex;
  justify-content:center;
  padding:clamp(1.5rem,5vh,4.5rem) var(--pad-x) 0;
  pointer-events:none;
  /* align-items deliberately left at `stretch`: the headline is top-anchored
     in the open sky, never centred over the turntable. */
}
.hero__inner a,
.hero__inner button{pointer-events:auto}

.headline{
  margin:0;
  font-family:var(--display);
  font-weight:400;
  font-size:clamp(2.7rem,6.3vw,6.75rem);
  line-height:1.06;
  letter-spacing:-.005em;
  text-align:center;
}

.line{
  display:block;
  overflow:hidden;
  white-space:nowrap;
  padding-bottom:.06em;
  margin-bottom:-.06em;
}
.line > span{
  display:block;
  transform:translateY(110%);
  opacity:0;
  animation:lineUp 1.25s var(--ease) forwards;
}
.line:nth-child(1) > span{animation-delay:.55s}
.line:nth-child(2) > span{animation-delay:.68s}

@keyframes lineUp{ to{ transform:translateY(0); opacity:1 } }

.hero__actions{
  display:flex;
  flex-wrap:wrap;
  justify-content:center;
  gap:.65rem;
  margin-top:1.35rem;
}

/* ---------------------------------------------------------- nav stagger -- */

.rise{
  opacity:0;
  transform:translateY(-14px);
  animation:rise .95s var(--ease) forwards;
}
@keyframes rise{ to{ opacity:1; transform:translateY(0) } }

.d1{animation-delay:.18s}
.d2{animation-delay:.26s}
.d3{animation-delay:.32s}
.d4{animation-delay:.38s}
.d5{animation-delay:.44s}
.d6{animation-delay:.52s}
.d7{animation-delay:.86s}
.d8{animation-delay:.96s}

/* A softer rise for content below the fold and inside the app. */
.lift{
  opacity:0;
  transform:translateY(16px);
  animation:lift .9s var(--ease) forwards;
}
@keyframes lift{ to{ opacity:1; transform:translateY(0) } }

/* ---------------------------------------------------------------- menu -- */

.menu{
  position:fixed;
  inset:0;
  z-index:50;
  display:flex;
  flex-direction:column;
  justify-content:center;
  padding:0 var(--pad-x);
  background:rgba(233,240,246,.92);
  backdrop-filter:blur(22px) saturate(140%);
  -webkit-backdrop-filter:blur(22px) saturate(140%);
  clip-path:inset(0 0 100% 0);
  visibility:hidden;
  transition:clip-path .85s var(--ease-in-out), visibility 0s linear .85s;
}
.menu.is-open{
  clip-path:inset(0 0 0 0);
  visibility:visible;
  transition:clip-path .85s var(--ease-in-out), visibility 0s linear 0s;
}

.menu__list{list-style:none;margin:0;padding:0}
.menu__list li{overflow:hidden}
.menu__list a{
  display:block;
  font-family:var(--display);
  font-size:clamp(2.35rem,12vw,3.4rem);
  line-height:1.22;
  padding:.12em 0;
  transform:translateY(115%);
  opacity:0;
  transition:transform .8s var(--ease), opacity .6s ease;
}
.menu.is-open .menu__list a{transform:translateY(0);opacity:1}
.menu.is-open .menu__list li:nth-child(1) a{transition-delay:.26s}
.menu.is-open .menu__list li:nth-child(2) a{transition-delay:.33s}
.menu.is-open .menu__list li:nth-child(3) a{transition-delay:.40s}
.menu.is-open .menu__list li:nth-child(4) a{transition-delay:.47s}
.menu.is-open .menu__list li:nth-child(5) a{transition-delay:.54s}

.menu__rule{
  height:1px;
  background:var(--line);
  margin:2.2rem 0 0;
  transform:scaleX(0);
  transform-origin:left;
  transition:transform .9s var(--ease);
}
.menu.is-open .menu__rule{transform:scaleX(1);transition-delay:.5s}

.menu__foot{
  margin-top:2.2rem;
  opacity:0;
  transform:translateY(22px);
  transition:transform .8s var(--ease), opacity .6s ease;
}
.menu.is-open .menu__foot{opacity:1;transform:translateY(0);transition-delay:.56s}
.menu__foot .btn{display:inline-flex;padding:.95rem 1.6rem;font-size:1rem}

/* ---------------------------------------------------------- responsive -- */

@media (max-width:900px){
  .nav__links{display:none}
  .nav__right .btn{display:none}
  .burger{display:block}

  .hero__media img,
  .hero__media video{object-position:center 60%}

  .hero__inner{align-items:flex-start}
  .line{white-space:normal}
  .headline{font-size:clamp(2.4rem,6.4vw,3.6rem);max-width:15ch;margin-inline:auto}

}

/* A 16:9 clip stretched over a tall phone crops the sky away. Bottom-anchor
   the footage at a sane scale and feather its top edge into the backdrop. */
@media (max-width:900px) and (orientation:portrait){
  .hero__media::before{display:block}
  .hero__media img,
  .hero__media video{
    top:auto;
    bottom:0;
    height:64svh;
    object-position:center 62%;
    -webkit-mask-image:linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.6) 9%, #000 20%);
    mask-image:linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.6) 9%, #000 20%);
  }
}

@media (max-width:560px){
  .headline{font-size:clamp(2.15rem,9.4vw,3.1rem);line-height:1.12;max-width:13ch}
  .hero__inner{padding-top:clamp(1.75rem,7vh,3rem)}
  .hero__media img,
  .hero__media video{object-position:center 58%}
  .hero__actions{flex-direction:column;width:100%}
  .hero__actions .btn{width:100%}
}

@media (max-width:380px){
  .headline{font-size:2rem}
}

@media (max-height:520px) and (orientation:landscape){
  .headline{font-size:clamp(1.7rem,5vw,2.4rem);max-width:22ch}
  .hero__inner{padding-top:.75rem}
  .menu__list a{font-size:clamp(1.4rem,5vw,1.9rem)}
  .menu__rule{margin-top:1.2rem}
  .menu__foot{margin-top:1.2rem}
}

/* Phones and small machines (ui/motion.js markLiteDevice): the breeze is
   off, and the layers the dive moves are composited up front. */
.hero-lite .hero__breeze{display:none}
.hero-lite .hero__media,
.hero-lite .hero__record,
.hero-lite .hero__record-disc{will-change:transform}

/* Each headline line wraps into even halves on a phone — "Send money /
   as fast", not "Send money as / fast". */
@media (max-width:560px){
  .line,.line > span{text-wrap:balance}
}

@media (prefers-reduced-motion:reduce){
  /* No dive (ui/vinyl.js returns early), so no tall section to scroll
     through to reach nothing: the hero is one screen. */
  .hero-scroll{height:100svh}
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-delay:0ms !important;
    transition-duration:.01ms !important;
  }
  .hero__media{opacity:1;transform:none;filter:none}
  .line > span{transform:none;opacity:1}
  .rise,.lift{opacity:1;transform:none}
  /* A drifting displacement field is motion, whatever it is drifting over. */
  .hero__breeze{display:none}
}

/* ------------------------------------------------------- accessibility -- */

:focus-visible{
  outline:2px solid var(--ink);
  outline-offset:2px;
  border-radius:4px;
}

.visually-hidden{
  position:absolute !important;
  width:1px;height:1px;
  padding:0;margin:-1px;
  overflow:hidden;
  clip:rect(0 0 0 0);
  clip-path:inset(50%);
  white-space:nowrap;
  border:0;
}

.skip-link{
  position:absolute;
  left:-9999px;
  top:0;
  z-index:200;
  padding:10px 16px;
  background:var(--ink);
  color:#fff;
  border-radius:0 0 6px 0;
}
.skip-link:focus{left:0}
