/* Lazy Hippo — site styles.
   Everything here uses tokens.css. No hard-coded colours. */

/* --- Reset ---------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--c-surface);
  color: var(--c-ink);
  font-family: var(--f-sans);
  font-size: var(--t-base);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  /* Full-width bands break out of .wrap with 100vw, which includes the
     scrollbar. Clip (not hide) so sticky positioning still works. */
  overflow-x: clip;
}

strong, b { font-weight: var(--w-medium); }

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

h1, h2, h3, p { margin: 0; }

a { color: inherit; }

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
  background: none;
  border: none;
}

/* One focus ring everywhere: 2px sage, 3px out. */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--c-sage);
  outline-offset: 3px;
}

/* Headlines are the light serif. Never bold. */
.display {
  font-family: var(--f-serif);
  font-weight: var(--w-light);
  line-height: var(--lh-tight);
  text-wrap: balance;
}

/* Uppercase kicker above a headline. */
.eyebrow {
  display: block;
  font-size: 12px;
  line-height: 16px;
  font-weight: var(--w-medium);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-ink-muted);
  margin-bottom: var(--s-3);
}

/* --- Language toggle ------------------------------------------------ */
.lang-toggle {
  flex: none;
  margin-left: auto;
  padding: var(--s-2) var(--s-4);
  font-size: var(--t-xs);
  font-weight: var(--w-medium);
  letter-spacing: 0.08em;
  color: var(--c-ink-muted);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--r-pill);
  transition: color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}

.lang-toggle:hover {
  color: var(--c-ink);
  border-color: var(--c-ink-muted);
}

/* --- Site header ---------------------------------------------------- */
/* Sits directly on the page surface — no bar colour, border or shadow.
   It still needs a fill because it is sticky and content scrolls under it.
   The inner div keeps its contents aligned with the page below. */
.site-head {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--c-surface);
}

.site-head__inner {
  display: flex;
  align-items: center;
  gap: var(--s-6);
  max-width: var(--wrap);
  margin-inline: auto;
  padding: 15px var(--gutter);
}

.site-head__logo { line-height: 0; flex: none; }
.site-head__logo img { width: 176px; height: auto; }

.site-nav {
  display: flex;
  gap: var(--s-12);
  flex-wrap: wrap;
  margin-left: var(--s-12);
}

.site-nav__link {
  font-size: var(--t-sm);
  line-height: 20px;
  color: var(--c-ink);
  text-decoration: none;
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur) var(--ease);
}
.site-nav__link:hover { border-bottom-color: var(--c-line); }
.site-nav__link--on,
.site-nav__link--on:hover { border-bottom-color: var(--c-ink); }

@media (max-width: 620px) {
  /* Three columns: empty | logo | toggle. The logo lands dead centre and
     the toggle stays on the same row, in normal flow rather than floating.
     The nav is a second row spanning the full width.

     Row 1 is a fixed --head-h track, the same height as the desktop bar, so
     the logo row matches desktop exactly. The nav row adds 32px on top of
     that when open, and collapses back to nothing on scroll:
     64px scrolled, 96px at the top of the page. */
  .site-head__inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    grid-template-rows: var(--head-h) auto;
    align-items: center;
    gap: 0;
    padding: 0 var(--s-4);
  }

  /* Rows are explicit. The nav sits between the logo and the toggle in the
     DOM, so without this grid auto-flow pushes the toggle to a third row. */
  .site-head__logo { grid-column: 2; grid-row: 1; margin: 0; }
  .site-head__logo img { width: 148px; }

  .site-head .lang-toggle {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    margin: 0;
  }

  .site-nav {
    grid-column: 1 / -1;
    grid-row: 2;
    width: 100%;
    margin: 0;
    gap: var(--s-6);
    justify-content: center;
    /* border-box, so this is 24px of links plus the 8px below them. */
    height: 32px;
    max-height: 32px;
    opacity: 1;
    overflow: hidden;
    align-items: center;
    padding-bottom: var(--s-2);
    transition: max-height var(--dur) var(--ease),
                opacity var(--dur) var(--ease),
                padding-bottom var(--dur) var(--ease);
  }

  /* The links carry the site's 1.65 body line-height, which is taller than
     the 24px row. Pin it here so the row height is exact and nothing clips. */
  .site-nav__link { line-height: 20px; }

  /* Tucked away while scrolling down. */
  .site-head--nav-hidden .site-nav {
    max-height: 0;
    opacity: 0;
    padding-bottom: 0;
  }
}

/* --- Page furniture -------------------------------------------------- */
.wrap {
  flex: 1;
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding: var(--s-6) var(--gutter) var(--s-24);
}

/* Intro paragraph under a section title: body-lg, headline → paragraph is
   space-6, paragraph → content is space-12. */
.page-lede {
  font-size: var(--t-lg);
  line-height: 30px;
  color: var(--c-ink-muted);
  max-width: var(--measure);
  text-wrap: pretty;
  margin-bottom: var(--s-12);
}

.state { color: var(--c-ink-muted); padding: var(--s-12) 0; }

.back {
  display: inline-block;
  font-size: var(--t-sm);
  color: var(--c-ink-muted);
  text-decoration: none;
  margin-bottom: var(--s-6);
}
.back:hover { color: var(--c-ink); }

/* --- Product grid ---------------------------------------------------- */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--s-12) var(--s-8);
}

.card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
}

.card__media {
  position: relative;
  aspect-ratio: 1;
  background: var(--c-paper-deep);
  border-radius: var(--r-md);
  overflow: hidden;
  margin-bottom: var(--s-6);
}

.card__media img {
  width: 100%; height: 100%;
  object-fit: contain;
  transition: transform var(--dur) var(--ease);
}
.card:hover .card__media img { transform: scale(1.03); }

.card__placeholder { width: 100%; height: 100%; background: var(--c-line); }

.card--sold .card__media img { opacity: 0.55; }

.card__badge {
  position: absolute;
  top: var(--s-3); left: var(--s-3);
  background: var(--c-ink);
  color: var(--c-surface);
  font-size: 11px;
  padding: 3px 10px;
  border-radius: var(--r-pill);
}

.card__name {
  font-size: var(--t-xl);
  line-height: 28px;
  font-weight: 400;
  color: var(--c-ink-soft);
  margin-bottom: var(--s-1);
}

/* Taglines stay on one line. If one is too long that's a copy problem,
   not a layout problem — keep them under about 36 characters. */
.card__tagline {
  font-size: var(--t-sm);
  color: var(--c-ink-muted);
  margin-bottom: var(--s-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card__price { display: flex; align-items: baseline; gap: var(--s-2); }

.price { font-weight: var(--w-medium); }
.price--lg { font-size: var(--t-xl); }
.price--was { color: var(--c-ink-muted); font-size: var(--t-sm); }

.card__swatches { display: flex; gap: var(--s-2); margin-top: var(--s-3); }
.swatch {
  width: 16px; height: 16px;
  border-radius: 50%;
  border: 1px solid rgba(27,33,31,.18);
}

/* --- Product detail --------------------------------------------------- */
.detail {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-8);
}

@media (min-width: 780px) {
  .detail { grid-template-columns: 1.1fr 1fr; gap: var(--s-16); align-items: start; }
}

.detail__media {
  aspect-ratio: 1;
  background: var(--c-paper-deep);
  border-radius: var(--r-xl);
  overflow: hidden;
}
.detail__media img { width: 100%; height: 100%; object-fit: contain; }

.detail__name {
  font-family: var(--f-serif);
  font-weight: var(--w-light);
  line-height: var(--lh-tight);
  font-size: var(--t-2xl);
  letter-spacing: -0.015em;
  margin-bottom: var(--s-3);
}

.detail__tagline { font-size: var(--t-lg); color: var(--c-ink-muted); margin-bottom: var(--s-6); }

.detail__price {
  display: flex; align-items: baseline; gap: var(--s-3);
  margin-bottom: var(--s-8);
}

.badge {
  background: var(--c-sage-soft);
  color: var(--c-ink);
  font-size: 11px;
  padding: 3px 9px;
  border-radius: var(--r-pill);
}

.variants { margin-bottom: var(--s-6); }
.variants__label {
  display: block;
  font-size: 12px;
  font-weight: var(--w-medium);
  color: var(--c-ink-muted);
  margin-bottom: var(--s-3);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.variants__row { display: flex; gap: var(--s-3); flex-wrap: wrap; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--c-line);
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
  transition: border-color var(--dur) var(--ease);
}
.chip:hover { border-color: var(--c-ink-muted); }
.chip--on { border-color: var(--c-ink); }
.chip--out { opacity: 0.45; }
.chip--out .chip__name { text-decoration: line-through; }

.chip__dot {
  width: 15px; height: 15px;
  border-radius: 50%;
  border: 1px solid rgba(27,33,31,.18);
}

.stock { font-size: var(--t-sm); margin-bottom: var(--s-4); }
.stock--low { color: var(--c-warn); }
.stock--out { color: var(--c-error); }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: var(--control-md);
  padding: var(--s-3) var(--s-8);
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
  line-height: 20px;
  font-weight: var(--w-medium);
  text-decoration: none;
  border: 1px solid transparent;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
/* Hero and section calls to action. */
.btn--lg { min-height: var(--control-lg); }
/* Actions that move the reader forward carry a thin arrow. */
.btn--go::after { content: "\2192"; font-weight: 400; }

/* One filled button per view. */
.btn--primary { background: var(--c-sage); color: var(--c-on-sage); }
.btn--primary:hover { background: var(--c-sage-deep); }
.btn[aria-disabled="true"] {
  background: var(--c-line);
  color: var(--c-ink-muted);
  pointer-events: none;
}

.detail__challenge {
  font-size: var(--t-sm);
  color: var(--c-ink-muted);
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--c-line);
}

.detail__desc {
  margin-top: var(--s-6);
  color: var(--c-ink-muted);
  text-wrap: pretty;
  max-width: var(--measure);
}

/* --- About page ------------------------------------------------------ */
/* Text on one side, image on the other. This is what fills a wide screen —
   stretching a paragraph across 1200px only makes it thin. */
.split {
  display: grid;
  gap: var(--s-8);
  align-items: center;
  padding: var(--s-8) 0 var(--s-16);
}
@media (min-width: 820px) {
  .split { grid-template-columns: 1.05fr 1fr; gap: var(--s-16); }
}
.split__t {
  font-family: var(--f-serif);
  font-size: var(--t-3xl);
  font-weight: var(--w-light);
  line-height: var(--lh-tight);
  letter-spacing: -0.02em;
  text-wrap: balance;
  margin-bottom: var(--s-6);
}
.split__lede { font-size: var(--t-lg); line-height: 30px; color: var(--c-ink-muted); text-wrap: pretty; }
/* The hero image box is identical on the homepage and About, so navigating
   between them doesn't make the photo jump or resize. */
.split__art,
.home-hero__art {
  margin: 0;
  aspect-ratio: 3 / 4;
}
.split__art img,
.home-hero__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
  display: block;
  border-radius: var(--r-xl);
  background: var(--c-surface-raised);
}

/* Full-width photo between sections. */
.band-img { margin: 0 0 var(--s-24); }
.band-img img {
  width: 100%; height: auto; display: block;
  border-radius: var(--r-xl);
  background: var(--c-surface-raised);
}

/* --- Organic shapes ---------------------------------------------------
   Soft, hand-drawn-looking blobs that sit BEHIND photography so pages
   don't read as a grid of rectangles. Decorative only: sage-mist for the
   big shape, sage-soft for the small pebble, never any text on them.
   The frame is position: relative + isolation, so the shapes' z-index -1
   stays behind the photo but above the page. Rounded corners live on the
   img, which is why the frames no longer clip their overflow. */
.split__art,
.home-hero__art,
.band-img {
  position: relative;
  isolation: isolate;
}
.split__art::before,
.home-hero__art::before,
.split__art::after,
.home-hero__art::after,
.band-img::before,
.band-img::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;
}

/* Portrait photos: a big blob peeking out left, right and below, and a
   pebble tucked under the bottom-left corner. */
.split__art::before,
.home-hero__art::before {
  top: 20%; right: -7%; bottom: -6%; left: -9%;
  background: var(--c-sage-mist);
  border-radius: 46% 54% 42% 58% / 38% 44% 56% 62%;
}
.split__art::after,
.home-hero__art::after {
  left: -14%; top: 88%;
  width: 34%; height: 20%;
  background: var(--c-sage-soft);
  border-radius: 58% 42% 50% 50% / 55% 60% 40% 45%;
}

/* The landscape photo gets a lighter touch: one shape rising behind the
   top-right corner, one pebble under the bottom-left. */
.band-img::before {
  top: -8%; right: -3%;
  width: 30%; height: 48%;
  background: var(--c-sage-mist);
  border-radius: 62% 38% 55% 45% / 48% 58% 42% 52%;
}
.band-img::after {
  left: -3%; bottom: -10%;
  width: 18%; height: 30%;
  background: var(--c-sage-soft);
  border-radius: 50% 50% 44% 56% / 60% 46% 54% 40%;
}

/* On phones the gutter is only 16px, so the shapes pull in to stay
   mostly on screen rather than being sliced off by the page edge. */
@media (max-width: 620px) {
  .split__art::before { left: -3%; right: -3%; }
  .split__art::after  { left: -3%; width: 30%; }
  .band-img::before   { right: -2%; }
  .band-img::after    { left: -2%; }
}

/* Two columns, so the text fills the page without any line running long. */
.story { margin-bottom: var(--s-24); }
.story__t {
  font-family: var(--f-serif);
  font-weight: var(--w-light);
  line-height: var(--lh-tight);
  font-size: var(--t-2xl);
  letter-spacing: -0.015em;
  margin-bottom: var(--s-6);
}
.story__cols { display: grid; gap: var(--s-6); color: var(--c-ink-muted); }
@media (min-width: 820px) {
  .story__cols { grid-template-columns: 1fr 1fr; gap: var(--s-12); }
}

.pillars-section { margin-bottom: var(--s-24); }

/* Benefit blocks: no card backgrounds, borders or shadows — just type. */
.pillars { display: grid; gap: var(--s-8); }
@media (min-width: 720px) { .pillars { grid-template-columns: repeat(3, 1fr); gap: var(--s-12); } }

.pillar { display: flex; flex-direction: column; }
.pillar h3 { font-size: var(--t-xl); line-height: 28px; font-weight: 400; color: var(--c-ink-soft); margin-bottom: var(--s-3); }
.pillar p { font-size: var(--t-sm); line-height: 23px; color: var(--c-ink-muted); }

/* --- Challenge page --------------------------------------------------- */

.section-title {
  font-family: var(--f-serif);
  font-weight: var(--w-light);
  line-height: var(--lh-tight);
  font-size: var(--t-2xl);
  letter-spacing: -0.015em;
  margin-bottom: var(--s-6);
}

.steps { display: grid; gap: var(--s-8); margin-bottom: var(--s-24); }
@media (min-width: 720px) { .steps { grid-template-columns: repeat(3, 1fr); gap: var(--s-12); } }

/* Numbers sit in the tinted icon discs, in the system's order. */
.step__n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-disc); height: var(--icon-disc);
  border-radius: 50%;
  background: var(--c-tint-sage);
  color: var(--c-icon-sage);
  font-family: var(--f-serif);
  font-size: 28px;
  font-weight: var(--w-light);
  margin-bottom: var(--s-6);
}
.step:nth-child(4n+2) .step__n { background: var(--c-tint-blush); color: var(--c-icon-clay); }
.step:nth-child(4n+3) .step__n { background: var(--c-tint-lilac); color: var(--c-icon-plum); }
.step:nth-child(4n+4) .step__n { background: var(--c-tint-mist);  color: var(--c-icon-slate); }
.step__t { font-size: var(--t-xl); line-height: 28px; font-weight: 400; color: var(--c-ink-soft); margin-bottom: var(--s-3); }
.step__b { font-size: var(--t-sm); line-height: 23px; color: var(--c-ink-muted); }

/* --- Lead capture ----------------------------------------------------- */
.capture {
  max-width: none;
}
.capture__t {
  font-family: var(--f-serif); font-weight: var(--w-light); line-height: var(--lh-tight);
  font-size: var(--t-2xl); letter-spacing: -0.015em; margin-bottom: var(--s-6);
}
.capture__b { color: var(--c-ink-muted); font-size: var(--t-lg); line-height: 30px; margin-bottom: var(--s-8); max-width: var(--measure); }

.capture__form { display: grid; gap: var(--s-4); max-width: 560px; }
.field { display: grid; gap: var(--s-2); }
.field__l { font-size: 12px; font-weight: var(--w-medium); color: var(--c-ink-muted); letter-spacing: 0.16em; text-transform: uppercase; }
.field__i {
  font: inherit;
  font-size: var(--t-base);
  min-height: var(--control-md);
  padding: var(--s-3) var(--s-6);
  border: 1px solid var(--c-line);
  border-radius: var(--r-pill);
  background: var(--c-surface);
  color: var(--c-ink);
  transition: border-color var(--dur) var(--ease);
}
.field__i:hover { border-color: var(--c-ink-muted); }
.field__i:focus { outline: 2px solid var(--c-sage); outline-offset: 3px; }
.field__i::placeholder { color: var(--c-ink-faint); }

.capture__form .btn { justify-self: start; margin-top: var(--s-2); }
.capture__form .btn:disabled { background: var(--c-line); color: var(--c-ink-muted); cursor: default; }

.capture__msg { font-size: var(--t-sm); min-height: 1.4em; margin: 0; }
.capture__msg--ok   { color: var(--c-ok); }
.capture__msg--warn { color: var(--c-error); }
.capture__fine { font-size: var(--t-xs); color: var(--c-ink-muted); margin: 0; }

/* --- Footer --------------------------------------------------------- */
.foot {
  display: flex;
  gap: var(--s-4);
  justify-content: center;
  flex-wrap: wrap;
  padding: var(--s-8) var(--gutter);
  font-size: var(--t-xs);
  color: var(--c-ink-muted);
}
.foot__wa { color: var(--c-ink-muted); text-decoration: none; }
.foot__wa:hover { color: var(--c-ink); }

/* --- Homepage --------------------------------------------------------- */
.home-hero {
  display: grid;
  gap: var(--s-8);
  align-items: center;
  padding: var(--s-12) 0 var(--s-24);
}
@media (min-width: 820px) {
  .home-hero { grid-template-columns: 1.05fr 1fr; gap: var(--s-16); }
}

.home-hero__t {
  font-family: var(--f-serif);
  font-size: var(--t-3xl);
  font-weight: var(--w-light);
  line-height: var(--lh-tight);
  letter-spacing: -0.02em;
  text-wrap: balance;
  margin-bottom: var(--s-6);
}
.home-hero__b { color: var(--c-ink-muted); font-size: var(--t-lg); line-height: 30px; max-width: var(--measure); }
.home-hero__cta { display: flex; gap: var(--s-4); flex-wrap: wrap; margin-top: var(--s-8); }
.home-hero__art { display: none; }
@media (min-width: 820px) { .home-hero__art { display: block; } }

.home-section { margin-bottom: var(--s-24); }
.home-section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  margin-bottom: var(--s-12);
}
.home-section__head .section-title { margin-bottom: 0; }
.link-more {
  font-size: var(--t-sm); font-weight: var(--w-medium); color: var(--c-ink);
  text-decoration: none; white-space: nowrap;
  padding-bottom: 2px; border-bottom: 1px solid var(--c-line);
  transition: border-color var(--dur) var(--ease);
}
.link-more:hover { border-bottom-color: var(--c-ink); }

/* Full-width tonal bands. Sections separate by tone, never by rules or
   boxed cards, so these break out of .wrap to the edges of the page. */
.home-band,
.capture {
  margin-inline: calc(50% - 50vw);
  padding: var(--s-24) max(var(--gutter), calc(50vw - var(--wrap) / 2 + var(--gutter)));
  background: var(--c-sage-mist);
}
.home-band { text-align: center; }

/* Two quiet shapes drifting in from the band's corners, half off the page.
   Same placement on every band: the small shape top-left, the larger one
   bottom-right. On phones they shrink right down. */
.home-band,
.capture { position: relative; isolation: isolate; overflow: hidden; }
.home-band::before,
.home-band::after,
.capture::before,
.capture::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;
  background: var(--c-sage-soft);
}
.home-band::before,
.capture::before {
  top: -70px; left: -60px;
  width: 260px; height: 200px;
  border-radius: 58% 42% 50% 50% / 55% 60% 40% 45%;
}
.home-band::after,
.capture::after {
  bottom: -90px; right: -40px;
  width: 320px; height: 240px;
  border-radius: 46% 54% 42% 58% / 38% 44% 56% 62%;
  opacity: 0.7;
}
@media (max-width: 820px) {
  .home-band::before,
  .capture::before { width: 140px; height: 100px; top: -50px; left: -50px; }
  .home-band::after,
  .capture::after  { width: 170px; height: 120px; bottom: -70px; right: -50px; }
}

/* ink-muted drops to ~4:1 on sage-mist. Inside mist panels, "muted" text
   steps up to ink-soft so body copy keeps 4.5:1. */
.home-band, .capture, .ch-card { --c-ink-muted: var(--c-ink-soft); }
.wrap > .home-band:last-child,
.wrap > .capture:last-child { margin-bottom: calc(var(--s-24) * -1); }

.home-band__t {
  font-family: var(--f-serif);
  font-weight: var(--w-light);
  line-height: var(--lh-tight);
  font-size: var(--t-2xl);
  letter-spacing: -0.015em;
  margin-bottom: var(--s-6);
  text-wrap: balance;
}
.home-band__b {
  color: var(--c-ink);
  font-size: var(--t-lg);
  line-height: 30px;
  max-width: 60ch;
  margin: 0 auto var(--s-8);
}

/* Outline — the softer path beside a primary. The border is faint (1.3x)
   by design; the label carries the meaning. */
.btn--secondary { background: transparent; color: var(--c-ink); border-color: var(--c-line); }
.btn--secondary:hover { border-color: var(--c-ink-muted); }
/* On a sage-mist band the hairline disappears, so the outline darkens. */
.home-band .btn--secondary { border-color: var(--c-ink-muted); }
.home-band .btn--secondary:hover { border-color: var(--c-ink); }

/* WhatsApp order button — brand green, so it reads as "message" not "buy". */
.btn--wa { background: #25D366; color: #073B22; }
.btn--wa:hover { background: #1FB855; color: #062E1B; }

/* Tighter gutters on phones — the content is the point, not the margin. */
@media (max-width: 620px) {
  .wrap { padding: var(--s-4) var(--gutter) var(--s-16); }
  .home-band, .capture { padding-block: var(--s-16); }
  .wrap > .home-band:last-child,
  .wrap > .capture:last-child { margin-bottom: calc(var(--s-16) * -1); }
}

/* --- Challenge card ---------------------------------------------------- */
/* One card holds the whole day: plan name, the 30 day circles, the day, its
   motivation, and a carousel of movement cards. */

.ch { margin-bottom: var(--s-24); }

.ch-card {
  background: var(--c-paper-deep);
  border-radius: var(--r-xl);
  padding: var(--s-12) var(--s-8);
}

/* Plan name as a dropdown, centred above the days. */
/* Header: plan on the left, progress on the right, rule underneath. */
/* Header: eyebrow on its own line, then a single row of plan name and day
   strip. Both are one line tall, so they align on the same centre — which is
   the whole reason the eyebrow is not inside the left column.
   Keeping the days up here also means the page carries a single pair of
   prominent arrows, the carousel's, so they are never ambiguous. */
.ch-eyebrow {
  font-size: 12px; font-weight: var(--w-medium); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--c-ink-muted); margin-bottom: var(--s-2);
  display: flex; align-items: baseline; gap: var(--s-2);
}

/* The only place the page admits whether you are signed in — progress saves
   silently, and a signed-out visitor sees an identical screen until they press
   the button. The word doubles as the state: "Sign out" can only mean you are
   in, "Sign in" can only mean you are out. */
.ch-who {
  font: inherit;
  margin-left: auto;
  color: var(--c-ink);
  letter-spacing: 0.16em;
  white-space: nowrap;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.ch-who:hover { text-decoration-color: var(--c-ink-muted); }

.ch-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4);
  padding-bottom: var(--s-5); margin-bottom: var(--s-8);
  border-bottom: 1px solid var(--c-line);
}

/* The name is never truncated at desktop widths — dayWin() has already cut
   the circle count so that both fit. Squeezing is a phone-only last resort,
   handled in the media query at the bottom. */
.ch-plan { position: relative; }
.ch-plan__btn > span { white-space: nowrap; }

.ch-plan__btn {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-family: var(--f-serif); font-size: var(--t-xl); font-weight: 400; color: var(--c-ink);
  padding: var(--s-2) var(--s-3);
  margin-left: calc(var(--s-3) * -1);
  border: 1px solid transparent; border-radius: var(--r-pill);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.ch-plan__btn:hover { border-color: var(--c-ink-faint); background: var(--c-surface); }
.ch-plan__chev { width: 12px; height: 8px; flex: none; color: var(--c-ink-faint); transition: transform var(--dur) var(--ease); }
.ch-plan__btn[aria-expanded="true"] .ch-plan__chev { transform: rotate(180deg); }
.ch-plan__btn[aria-expanded="true"] { border-color: var(--c-ink-faint); background: var(--c-surface); }

.ch-plan__menu {
  position: absolute; top: calc(100% + 6px); left: 0;
  z-index: 15; min-width: 260px;
  /* Also a <ul>: kill the default block margin as well as the indent. */
  list-style: none; margin: 0; padding: var(--s-2);
  background: var(--c-surface); border: 1px solid var(--c-line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-soft);
  font-family: var(--f-sans);
}
.ch-plan__menu[hidden] { display: none; }

.ch-plan__opt {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); text-align: left;
  font-size: var(--t-sm); color: var(--c-ink-muted);
  padding: var(--s-2) var(--s-3); border-radius: var(--r-sm);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.ch-plan__opt:hover { background: var(--c-sage-mist); color: var(--c-ink); }
.ch-plan__opt.is-on { color: var(--c-ink); font-weight: var(--w-medium); }
.ch-plan__opt.is-locked .ch-plan__label { color: var(--c-ink-faint); }

/* The i on a locked plan. Hovering says which product opens it. */
.ch-info {
  width: 17px; height: 17px; flex: none;
  border-radius: 50%; border: 1px solid var(--c-ink-faint);
  font-size: 11px; font-style: italic; line-height: 15px; text-align: center;
  color: var(--c-ink-faint); cursor: help; position: relative;
}
.ch-info::after {
  content: attr(data-tip);
  position: absolute; right: 0; top: calc(100% + 6px);
  width: max-content; max-width: 210px;
  background: var(--c-ink); color: var(--c-surface);
  font-size: 11px; font-style: normal; line-height: 1.5; text-align: left;
  padding: var(--s-2) var(--s-3); border-radius: var(--r-sm);
  opacity: 0; pointer-events: none; transition: opacity var(--dur) var(--ease);
  z-index: 20;
}
.ch-info:hover::after, .ch-info:focus::after { opacity: 1; }

/* The day strip. A window onto the 30, sized to the screen: fifteen on a
   desktop, ten on a tablet, seven on a phone. Arrows page a whole window. */
.ch-daystrip { display: flex; align-items: center; gap: var(--s-2); flex: none; }

.ch-days { display: flex; align-items: center; gap: 5px; }

.ch-day {
  width: 28px; height: 28px; flex: none;
  border-radius: 50%;
  font-size: 11px; line-height: 1;
  border: 1px solid var(--c-line); background: var(--c-surface);
  color: var(--c-ink-muted);
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.ch-day.is-open:hover { border-color: var(--c-ink-muted); transform: translateY(-1px); }
/* A finished day keeps the ordinary circle. It used to fill sage green, which
   put a second accent colour in a strip that already uses rose for today —
   two competing signals in twelve pixels. The count in the eyebrow carries
   the progress instead. */
.ch-day.is-done { color: var(--c-ink); border-color: var(--c-line); }
.ch-day.is-locked { border-style: dashed; color: var(--c-ink-faint); opacity: 0.5; cursor: not-allowed; }
.ch-day.is-current {
  background: var(--c-sage); border-color: var(--c-sage); color: var(--c-on-sage);
  font-weight: var(--w-medium);
}

.ch-head { margin-bottom: var(--s-6); }
.ch-daynum { font-family: var(--f-serif); font-weight: var(--w-light); font-size: var(--t-2xl); line-height: var(--lh-tight); letter-spacing: -0.015em; }
.ch-daynum span { color: var(--c-ink-muted); }
.ch-motivation {
  font-size: var(--t-lg); line-height: 30px; color: var(--c-ink);
  text-wrap: pretty; margin-top: var(--s-3); max-width: 62ch;
}

/* Movement carousel. Three across, sliding, arrows outside the window. */
.ch-carousel { display: flex; align-items: center; gap: var(--s-2); }

/* The window is a separate element from the strip. Putting overflow on the
   strip itself clips against a box that slides along with the cards, which
   pushes them outside the card entirely. */
.ch-viewport { flex: 1; min-width: 0; overflow: hidden; }

.ch-moves {
  --per: 3;
  /* It is an <ol>, so it carries a 40px padding-inline-start by default.
     list-style: none does not remove that — it only hides the markers — and
     the leftover indent pushed the whole strip right of centre. */
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-auto-flow: column;
  grid-auto-columns: calc((100% - (var(--per) - 1) * var(--s-4)) / var(--per));
  gap: var(--s-4);
  transition: transform var(--dur) var(--ease);
}

/* One height for every card, on every day. Without a floor, a day of short
   cues gives short cards and a day of long ones gives tall cards, and the
   whole block jumps as you move between days. Sized for the worst case: a
   two-line name, the picture, and a four-line cue. Cards with no picture keep
   the height and simply have more air under the text. */
.ch-move {
  display: flex; flex-direction: column;
  min-height: 320px;
  background: var(--c-surface);
  border-radius: var(--r-md);
  padding: var(--s-6) var(--s-4) var(--s-4);
}

/* Illustration slot. Only present on movements that actually have a picture —
   the markup omits it entirely otherwise, so there is no empty box to style.

   No background fill: the figures are transparent, and a tinted panel behind a
   transparent figure just puts a box back inside a box. */
/* Bottom-aligned, not centred. Every figure is emitted with its mat flush to
   the bottom of the image, so aligning the boxes' bottoms puts every mat on
   one line across the row. Centring did not: a pair is constrained by width
   because the two share the card, a single by height, so the two render at
   different scales and their mats drifted apart vertically. */
.ch-move__art {
  height: 140px;
  display: flex; align-items: flex-end; justify-content: center;
  gap: var(--s-4);
  margin: var(--s-1) 0 var(--s-3);
  overflow: hidden;
}

/* Height is the constant, width follows. Every figure is emitted at the same
   pixel height by tools/split_figures.py, so matching heights here keeps the
   whole set at one scale no matter how wide or narrow a pose happens to be. */
.ch-move__art img {
  height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
}
/* A movement with two positions alternates between two frames in one footprint
   rather than showing both side by side. Both frames are stacked in the same
   place, so the figure stays exactly the size a single figure would be — and
   the movement is shown moving, which two static poses never quite managed.

   The frames are the same canvas with the figure in the same spot, so stacking
   them bottom-left-aligned registers them without any per-image nudging. */
.ch-move__art--anim { position: relative; }
.ch-move__art--anim img {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  animation: ch-swap 4s ease-in-out infinite;
}
/* Half a cycle behind, so exactly one frame is visible at any moment. */
.ch-move__art--anim img + img { animation-delay: -2s; }

@keyframes ch-swap {
  0%, 40%   { opacity: 1; }
  50%, 90%  { opacity: 0; }
  100%      { opacity: 1; }
}

/* Motion in a wellness product should never be forced on anyone. Without it
   the first position holds still and the cue text carries the rest. */
@media (prefers-reduced-motion: reduce) {
  .ch-move__art--anim img { animation: none; opacity: 1; }
  .ch-move__art--anim img + img { display: none; }
}

/* Name and dose stack rather than sharing a row. Side by side, a long name
   like "Lying Hamstring Stretch" wrapped around the dose and left an orphan
   word under it. Given the full width it wraps like a heading instead. */
.ch-move__head { margin-bottom: var(--s-2); }
.ch-move__n {
  display: block;
  font-size: var(--t-base); font-weight: 400; color: var(--c-ink-soft);
  line-height: 1.35; text-wrap: balance;
}
.ch-move__dose {
  display: block;
  font-size: 12px; font-weight: var(--w-medium); letter-spacing: 0.08em; color: var(--c-ink-muted);
  white-space: nowrap; margin-top: var(--s-1);
}
.ch-move__cue { font-size: var(--t-xs); color: var(--c-ink-muted); line-height: 1.6; }

.ch-arrow {
  width: 30px; height: 30px; flex: none;
  border-radius: 50%; border: 1px solid var(--c-ink-faint);
  background: var(--c-surface); color: var(--c-ink);
  font-size: var(--t-lg); line-height: 1;
  transition: border-color var(--dur) var(--ease);
}
.ch-arrow:hover:not(:disabled) { border-color: var(--c-ink-muted); }
.ch-arrow:disabled { opacity: 0.25; cursor: default; }
.ch-arrow--sm { width: 26px; height: 26px; font-size: var(--t-base); }
.ch-carousel.is-single .ch-arrow { visibility: hidden; }

.ch-count { font-size: 12px; color: var(--c-ink-muted); text-align: right; margin-top: var(--s-2); min-height: 1em; padding-right: 38px; }

.ch-safety {
  font-size: var(--t-xs); color: var(--c-ink-muted); text-align: center;
  margin-top: var(--s-8);
}

.ch-actions { text-align: center; margin-top: var(--s-6); }
.ch-hint { font-size: 12px; color: var(--c-ink-muted); margin-top: var(--s-3); min-height: 1em; }
/* Still waiting for you to look through the movements. Outlined rather than
   filled, so it reads as "not yet" without reading as broken — it is very
   much still pressable, and pressing it scrolls you on to what's left. */
.btn--waiting {
  background: transparent;
  color: var(--c-ink-muted);
  border: 1px solid var(--c-ink-faint);
}
.btn--waiting:hover { color: var(--c-ink); border-color: var(--c-ink-muted); }

/* A day already recorded. Deliberately flat and grey — it is not a button any
   more, it is a receipt, and it needs to look unmistakably different from the
   rose primary button beside it in every other state. */
.btn--done {
  background: var(--c-sage-soft);
  color: var(--c-ink);
  border: 1px solid var(--c-sage-soft);
  cursor: default;
}

.ch-locked { text-align: center; padding: var(--s-12) var(--s-4); }
.ch-locked__t { font-family: var(--f-serif); font-weight: var(--w-light); font-size: var(--t-xl); line-height: 28px; color: var(--c-ink); margin-bottom: var(--s-3); text-wrap: pretty; }
.ch-locked__b { font-size: var(--t-sm); color: var(--c-ink-muted); margin-bottom: var(--s-6); }

@media (max-width: 620px) {
  .ch-card { padding: var(--s-8) var(--s-4); border-radius: var(--r-md); }

  /* Same one-row header as everywhere else. The plan name drops a size and
     the circles shrink; dayWin() has already cut the count to three. */
  /* Five circles plus two arrows plus a plan name is a lot for 375px, so
     everything here is trimmed to buy the name back some room. It still
     truncates on the narrowest phones — that is the trade for five days. */
  .ch-bar { gap: var(--s-2); padding-bottom: var(--s-4); margin-bottom: var(--s-6); }

  /* Only here does the name give way, and only if five circles plus a long
     plan name genuinely will not fit. */
  .ch-plan { min-width: 0; }
  .ch-plan__btn { font-size: var(--t-base); padding: var(--s-2) 6px; margin-left: -6px; gap: 6px; min-width: 0; }
  .ch-plan__btn > span { overflow: hidden; text-overflow: ellipsis; }
  .ch-daystrip { gap: 3px; }
  .ch-days { gap: 3px; }
  .ch-day { width: 25px; height: 25px; font-size: 10px; }

  .ch-arrow { width: 32px; height: 32px; }
  .ch-arrow--sm { width: 21px; height: 21px; font-size: var(--t-sm); }
  .ch-move__art { height: 170px; }

  /* On a phone the side arrows were eating ~70px of a ~230px card, so the
     movement ended up narrower than the space either side of it. The strip
     gets its own full-width row and the arrows sit underneath it. */
  .ch-carousel { flex-wrap: wrap; justify-content: center; gap: var(--s-4) var(--s-6); }
  .ch-viewport { flex: 1 0 100%; order: 1; }
  #prevMove { order: 2; }
  #nextMove { order: 3; }
  .ch-count { order: 4; flex: 1 0 100%; text-align: center; padding-right: 0; margin-top: 0; }
}

/* --- Account sheet ----------------------------------------------------- */
.sheet {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(27, 31, 27, 0.45);
  display: flex; align-items: center; justify-content: center;
  padding: var(--s-4);
}
.sheet[hidden] { display: none; }
.sheet__panel {
  position: relative; width: 100%; max-width: 380px;
  background: var(--c-surface); border-radius: var(--r-xl);
  padding: var(--s-12) var(--s-8) var(--s-8);
}
.sheet__x {
  position: absolute; top: var(--s-3); right: var(--s-4);
  background: none; border: 0; font-size: 24px; line-height: 1; color: var(--c-ink-muted);
}
.sheet__t { font-family: var(--f-serif); font-weight: var(--w-light); font-size: 2rem; line-height: 1.1; letter-spacing: -0.015em; margin-bottom: var(--s-3); }
.sheet__b { font-size: var(--t-sm); color: var(--c-ink-muted); margin-bottom: var(--s-6); }
.sheet__or {
  text-align: center; font-size: var(--t-xs); color: var(--c-ink-muted);
  margin: var(--s-4) 0; position: relative;
}
.sheet__or::before {
  content: ''; position: absolute; top: 50%; left: 0; right: 0;
  height: 1px; background: var(--c-line);
}
.sheet__or span { position: relative; background: var(--c-surface); padding: 0 var(--s-3); }
.sheet__form .field { margin-bottom: var(--s-4); }
.sheet__form .btn { width: 100%; }
.sheet__msg { font-size: var(--t-xs); color: var(--c-ink-muted); margin-top: var(--s-3); min-height: 1em; }
.sheet__switch { font-size: var(--t-xs); color: var(--c-ink-muted); text-align: center; margin-top: var(--s-4); }
.linkish { background: none; border: 0; color: var(--c-ink); font-size: inherit; font-weight: var(--w-medium); text-decoration: underline; text-underline-offset: 3px; }
.btn--google { width: 100%; background: var(--c-surface); border: 1px solid var(--c-line); color: var(--c-ink); }
.btn--google:hover { border-color: var(--c-ink-muted); }

/* --- Collage hero ------------------------------------------------------
   The hero art on the products and challenge pages: cut-out images (the
   real products, or the pose illustrations) floating on the same blob pair
   as the photo heroes. Every
   layer is absolutely placed in a fixed-ratio box, so it scales as one. */
.collage {
  position: relative;
  isolation: isolate;
  aspect-ratio: 1 / 1.02;
}
.collage::before,
.collage::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;
}
.collage::before {
  inset: 8% 2% 6% 4%;
  background: var(--c-sage-mist);
  border-radius: 46% 54% 42% 58% / 38% 44% 56% 62%;
}
.collage::after {
  left: -4%; bottom: 0;
  width: 36%; height: 20%;
  background: var(--c-sage-soft);
  border-radius: 58% 42% 50% 50% / 55% 60% 40% 45%;
}
.collage__img { position: absolute; height: auto; max-width: none; }

/* The whole range in one square cut-out, centred on the blob. */
.collage--products .collage__img--range { width: 96%; left: 2%; top: 1%; }

/* One pose, centred on the blob (the image is 4:3, the box ~1:1). It runs
   past the box sides because the drawing has wide empty margins. */
.collage--poses .collage__img--main { width: 128%; left: -14%; top: 4%; }

/* The collage takes the photo's place, so it follows the same rule:
   hidden on phones, where the hero is copy only. */
.home-hero > .collage { display: none; }
@media (min-width: 820px) { .home-hero > .collage { display: block; } }

/* Steps inside a section that already has its own bottom margin. */
.steps--flush { margin-bottom: 0; }
.home-section > .eyebrow + .section-title { margin-bottom: var(--s-12); }
.home-section > .section-title:has(+ .page-lede) { margin-bottom: var(--s-6); }

/* --- Poses row (challenge page) ----------------------------------------
   Four illustrations, each on its own blob, so the row reads as a set of
   pebbles rather than a grid of boxes. */
.poses {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--s-8) var(--s-6);
}
@media (min-width: 820px) { .poses { grid-template-columns: repeat(4, 1fr); gap: var(--s-12); } }
.pose { margin: 0; text-align: center; }
.pose__art { position: relative; isolation: isolate; margin-bottom: var(--s-4); }
.pose__art::before {
  content: "";
  position: absolute;
  inset: 12% 4% 2%;
  z-index: -1;
  background: var(--c-sage-mist);
  border-radius: 46% 54% 42% 58% / 38% 44% 56% 62%;
}
.pose:nth-child(2) .pose__art::before { border-radius: 58% 42% 50% 50% / 55% 60% 40% 45%; }
.pose:nth-child(3) .pose__art::before { border-radius: 40% 60% 48% 52% / 52% 40% 60% 48%; }
.pose:nth-child(4) .pose__art::before { border-radius: 55% 45% 52% 48% / 46% 56% 44% 54%; }
.pose__art img { width: 100%; height: auto; }
.pose__n { font-size: var(--t-xl); line-height: 28px; color: var(--c-ink-soft); }

/* =====================================================================
   Motion
   Paired with js/motion.js. Every rule below is scoped to html.has-motion,
   which that file only sets when the visitor has NOT asked for reduced
   motion — so for them, and if the script fails, the site is simply still.
   Transforms are split across the individual properties on purpose:
   reveals use `translate`, parallax / tilt / magnetic pull use `transform`,
   pops use `scale` — so two effects on one element never overwrite each
   other.
   ===================================================================== */

/* Hides <main> for the instant between first paint and motion.js running,
   so content doesn't flash in and then vanish to be revealed. */
html.motion-pending main { opacity: 0; }

:root { --ease-out: cubic-bezier(0.2, 0.7, 0.1, 1); }

/* --- Reveals: blocks float up ----------------------------------------- */
html.has-motion .rv {
  opacity: 0;
  translate: 0 32px;
  transition: opacity 0.9s var(--ease-out) var(--d, 0ms),
              translate 1s var(--ease-out) var(--d, 0ms);
}
html.has-motion .rv.is-in { opacity: 1; translate: 0 0; }

/* --- Reveals: headlines rise word by word from behind a mask ---------- */
html.has-motion .rv-words .w {
  display: inline-block;
  overflow: clip;
  vertical-align: top;
  /* Room for descenders and the serif's overhangs inside the mask. */
  padding: 0.06em 0.04em 0.16em;
  margin: -0.06em -0.04em -0.16em;
}
html.has-motion .rv-words .w__i {
  display: inline-block;
  translate: 0 110%;
  transition: translate 1.1s var(--ease-out) calc(var(--d, 0ms) + var(--i, 0) * 70ms);
}
html.has-motion .rv-words.is-in .w__i { translate: 0 0; }

/* Challenge step discs pop in with a little overshoot. */
html.has-motion .step.rv .step__n {
  scale: 0.4;
  transition: scale 0.9s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--d, 0ms) + 150ms);
}
html.has-motion .step.rv.is-in .step__n { scale: 1; }

/* --- Parallax ----------------------------------------------------------
   --p comes from motion.js (-1 → 1 as the element crosses the screen).
   The photo, its big blob and its pebble each move by a different amount,
   so they separate into layers as you scroll. */
html.has-motion :is(.home-hero__art, .split__art) img {
  transform: translate3d(0, calc(var(--p, 0) * -28px), 0);
}
html.has-motion :is(.home-hero__art, .split__art)::before {
  transform: translate3d(0, calc(var(--p, 0) * 22px), 0);
}
html.has-motion :is(.home-hero__art, .split__art)::after {
  transform: translate3d(calc(var(--p, 0) * -10px), calc(var(--p, 0) * 56px), 0);
}
html.has-motion .band-img img  { transform: translate3d(0, calc(var(--p, 0) * -16px), 0); }
html.has-motion .band-img::before { transform: translate3d(0, calc(var(--p, 0) * 30px), 0); }
html.has-motion .band-img::after  { transform: translate3d(0, calc(var(--p, 0) * 50px), 0); }
html.has-motion .collage__img {
  transform: translate3d(0, calc(var(--p, 0) * var(--depth, 0) * -1px), 0);
}
html.has-motion .collage::before { transform: translate3d(0, calc(var(--p, 0) * 14px), 0); }
html.has-motion .collage::after  { transform: translate3d(0, calc(var(--p, 0) * 40px), 0); }
html.has-motion :is(.home-band, .capture)::before {
  transform: translate3d(0, calc(var(--p, 0) * -40px), 0);
}
html.has-motion :is(.home-band, .capture)::after {
  transform: translate3d(0, calc(var(--p, 0) * 50px), 0);
}

/* --- Blobs breathe -----------------------------------------------------
   Very slowly reshape, like something alive. Two rhythms so neighbouring
   shapes never move in step. */
@keyframes blob-a {
  0%   { border-radius: 46% 54% 42% 58% / 38% 44% 56% 62%; }
  50%  { border-radius: 55% 45% 52% 48% / 46% 56% 44% 54%; }
  100% { border-radius: 40% 60% 48% 52% / 52% 40% 60% 48%; }
}
@keyframes blob-b {
  0%   { border-radius: 58% 42% 50% 50% / 55% 60% 40% 45%; }
  50%  { border-radius: 48% 52% 60% 40% / 45% 50% 50% 55%; }
  100% { border-radius: 62% 38% 44% 56% / 58% 48% 52% 42%; }
}
html.has-motion :is(.home-hero__art, .split__art, .band-img, .home-band, .capture, .collage)::before {
  animation: blob-a 14s ease-in-out infinite alternate;
}
html.has-motion :is(.home-hero__art, .split__art, .band-img, .home-band, .capture, .collage)::after {
  animation: blob-b 11s ease-in-out infinite alternate;
}

/* Cut-outs bob very slightly, each on its own beat, as if floating.
   `translate` so it adds to the parallax `transform` instead of replacing it. */
@keyframes float {
  from { translate: 0 -5px; }
  to   { translate: 0 5px; }
}
html.has-motion .collage__img { animation: float 5s ease-in-out infinite alternate; }
html.has-motion .collage__img:nth-child(2) { animation-duration: 6.5s; animation-delay: -2s; }
html.has-motion .collage__img:nth-child(3) { animation-duration: 4.5s; animation-delay: -1s; }

/* Poses lift off their blob when hovered. */
html.has-motion .pose__art img { transition: translate 0.5s var(--ease-out); }
html.has-motion .pose:hover .pose__art img { translate: 0 -8px; }

/* --- Buttons -------------------------------------------------------------
   Pulled gently toward the pointer (--mx/--my from motion.js), a small
   squash when pressed, the arrow nudges forward, and outline buttons fill
   from the bottom on hover. */
html.has-motion .btn {
  transform: translate3d(var(--mx, 0px), var(--my, 0px), 0) scale(var(--s, 1));
  transition: background-color var(--dur) var(--ease),
              background-size 0.45s var(--ease-out),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              transform 0.4s var(--ease-out);
}
html.has-motion .btn:active { --s: 0.96; }
html.has-motion .btn--go::after { transition: translate 0.35s var(--ease-out); }
html.has-motion .btn--go:hover::after { translate: 4px 0; }

html.has-motion .btn--secondary {
  background-image: linear-gradient(var(--sweep, var(--c-sage-mist)), var(--sweep, var(--c-sage-mist)));
  background-position: 0 100%;
  background-size: 100% 0%;
  background-repeat: no-repeat;
}
html.has-motion .btn--secondary:hover { background-size: 100% 100%; }
/* On a sage-mist band the fill is the lighter page surface instead. */
html.has-motion :is(.home-band, .capture) { --sweep: var(--c-surface); }

/* --- Product cards: a slight 3D tilt toward the pointer ------------------ */
html.has-motion .card__media {
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform 0.5s var(--ease-out);
}
html.has-motion .swatch { transition: scale 0.25s var(--ease-out); }
html.has-motion .card:hover .swatch { scale: 1.15; }
html.has-motion .chip:active { scale: 0.97; }

/* --- Nav: underline draws in from the left ------------------------------- */
html.has-motion .site-nav__link {
  border-bottom-color: transparent !important;
  background: linear-gradient(currentColor, currentColor) 0 100% / 0% 1px no-repeat;
  transition: background-size 0.4s var(--ease-out);
}
html.has-motion .site-nav__link:hover,
html.has-motion .site-nav__link--on { background-size: 100% 1px; }

/* --- Cursor ---------------------------------------------------------------
   A ring trailing the real pointer. Over anything pressable it fills with
   a translucent white veil — same size, it never grows — and it gets out
   of the way over text fields. */
.cursor {
  position: fixed;
  top: 0; left: 0;
  width: 0; height: 0;
  z-index: 60;
  pointer-events: none;
}
.cursor::before {
  content: "";
  position: absolute;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 1px solid var(--c-ink-muted);
  border-radius: 50%;
  opacity: 0;
  transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease),
              opacity 0.3s var(--ease), scale 0.2s var(--ease-out);
}
.cursor.is-on::before { opacity: 0.55; }
.cursor.is-on.is-press::before {
  background: var(--c-veil);
  border-color: var(--c-ink-faint);
  opacity: 1;
}
.cursor.is-text::before { opacity: 0; }
.cursor.is-down::before { scale: 0.85; }

/* --- Reading progress ----------------------------------------------------- */
.progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2px;
  z-index: 30;
  background: var(--c-sage);
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}
