/* ==========================================================================
   the goo crew

   A scrolling page in three acts:

     1. the stage   — the character, standing in a pool of its own goo, with
                      the switch and everything it changes beside it
     2. the ask     — the pre-sale on one side, the three samples standing in
                      a line on the other
     3. what makes  — the claim beside seven drops of goo, nothing to open

   Picking a character retints every act — pool, tabs, drops, small type —
   because each character owns one of the three brand colour groups. That runs
   off `:checked` alone; js/crew.js only adds the sink/rise flourish and the
   scroll reveals.
   ========================================================================== */

/* Registering the goo colours as real colours is what lets them cross-fade
   when the character changes; a plain custom property would jump. */
@property --pool-a {
  syntax: "<color>";
  inherits: true;
  initial-value: #ebd0fb;
}

@property --pool-b {
  syntax: "<color>";
  inherits: true;
  initial-value: #ff9e8e;
}

@property --group-ink {
  syntax: "<color>";
  inherits: true;
  initial-value: #50341e;
}

@property --group-tint {
  syntax: "<color>";
  inherits: true;
  initial-value: rgba(219, 176, 250, 0.55);
}

.crewpage {
  position: relative;
  /* The goo pool bleeds past the character. `clip` trims it without making
     this a scroll container the way `hidden` would. */
  overflow-x: clip;
  background: var(--cream);

  --ease-out-back: cubic-bezier(0.22, 1.32, 0.36, 1);
  /* One vertical rhythm for the whole page: acts are one unit apart, and the
     blocks inside them are fractions of it. */
  --act-y: clamp(4rem, 10vh, 8rem);
}

/* The ambient wash sits quieter here than on the home page — the goo is the
   coloured thing on this page, and two washes competing turns both to mud.
   Fixed rather than absolute, so it holds still under the scrolling page
   instead of handing the document a screen's worth of overflow. */
.crew-page .hero__wash {
  position: fixed;
  inset: -14%;
  opacity: 0.24;
}

/* ---- Group palettes -----------------------------------------------------
   Each character owns one brand colour group: unigoo pink, dragoo green,
   honeygoo yellow. `--pool-a`/`--pool-b` are the two goo hues, `--group-ink`
   is the group's readable accent for small type, `--group-tint` its glow.

   The pink group has no dark member, so it borrows the universal ink rather
   than a colour invented for the occasion. */

.crewpage,
#char-unigoo:checked ~ * {
  --pool-a: #ebd0fb;
  --pool-b: #ff9e8e;
  --group-ink: #50341e;
  --group-tint: rgba(219, 176, 250, 0.55);
}

#char-dragoo:checked ~ * {
  --pool-a: #95d46c;
  --pool-b: #f39067;
  --group-ink: #597f2f;
  --group-tint: rgba(149, 212, 108, 0.6);
}

#char-honeygoo:checked ~ * {
  --pool-a: #ffce57;
  --pool-b: #b4ccd6;
  --group-ink: #50341e;
  --group-tint: rgba(255, 206, 87, 0.65);
}

/* The cross-fade is declared on the sections the values change on, so every
   descendant inherits the tween for free. */
.opening,
.stage,
.presale,
.made {
  transition:
    --pool-a 0.6s ease,
    --pool-b 0.6s ease,
    --group-ink 0.6s ease,
    --group-tint 0.6s ease;
}

/* Visually hidden, but `fixed` rather than the usual `absolute`.

   The radios have to be the first children of the page for `:checked ~ *` to
   reach every act, which parks them at the very top of the document. Clicking
   a tab activates its label's radio, and the browser scrolls a newly focused
   control into view — so every character switch threw the page back to the top
   before you could see the goo take the hit. Fixed, the control is always
   already in view and there is nothing to scroll to.

   `~` is a DOM combinator, so taking these out of flow costs the retint
   nothing. */
.cast__radio {
  position: fixed;
  top: 50%;
  left: 0;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   Scroll reveal

   Everything arrives the same way: up and in, once, when it first crosses
   into view. js/crew.js adds `.is-in`; without JS the `.reveal` rule never
   applies, because it is scoped to `.goo-js` on the root.
   ========================================================================== */

/* `translate`, not `transform`: they are separate properties, so an element
   that arrives this way can still own its `transform` for a hover lift or an
   idle animation without the two overwriting each other.

   The stagger comes through `--reveal-delay` rather than a `transition-delay`
   declaration, because a custom property inherits regardless of specificity —
   a plain `transition-delay` on a single-class selector loses to this rule's
   `transition` shorthand, which silently resets the delay to zero. */
.goo-js .reveal {
  opacity: 0;
  translate: 0 22px;
  transition:
    opacity 0.75s var(--ease-smooth) var(--reveal-delay, 0s),
    translate 0.75s var(--ease-smooth) var(--reveal-delay, 0s);
}

.goo-js .reveal.is-in {
  opacity: 1;
  translate: none;
}

@media (prefers-reduced-motion: reduce) {
  .goo-js .reveal {
    opacity: 1;
    translate: none;
  }
}

/* ==========================================================================
   Opening

   Eyebrow, title, one line of context — and then straight into the characters.
   It is deliberately not a screen of its own: the title and the stage below it
   are meant to be read as one opening view, so the page starts by showing you
   the crew rather than by asking you for something.
   ========================================================================== */

.opening {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 var(--space-3) clamp(1.75rem, 4.5vh, 3rem);
  text-align: center;
}

.opening__eyebrow {
  font-family: var(--font-display);
  font-weight: 500;
  text-transform: lowercase;
  letter-spacing: 0.14em;
  font-size: 0.85rem;
  color: var(--group-ink);
  margin: 0 0 var(--space-1);
}

.opening__title {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: lowercase;
  letter-spacing: -0.03em;
  font-size: clamp(2.6rem, 8vw, 5.25rem);
  line-height: 0.95;
  color: #131313;
  margin: 0;
}

/* A subtitle under the title, not a second headline — the brand sets titles
   semibold and subtitles medium, and two display lines at headline size stacked
   on top of each other read as an argument about which one is the heading. */
.opening__line {
  max-width: 24ch;
  margin: clamp(1rem, 2.5vw, 1.6rem) 0 0;
  font-family: var(--font-display);
  font-weight: 500;
  text-transform: lowercase;
  letter-spacing: -0.02em;
  font-size: clamp(1.1rem, 2.2vw, 1.5rem);
  line-height: 1.35;
  color: var(--ink);
  --reveal-delay: 0.08s;
}

.opening__line strong {
  font-weight: 600;
  color: var(--green-accent2);
}

/* ==========================================================================
   Act one — the stage

   The character on the left, the switch and the copy on the right. The pair
   is the whole act; nothing else competes for the row.
   ========================================================================== */

.stage {
  position: relative;
  z-index: 2;
  padding: 0 var(--space-3) var(--act-y);
}

/* Two tracks sized to their contents rather than to fractions of the row, and
   the pair centred as one object. Fractional tracks left the character adrift
   in a column much wider than the artwork — the crop is tall and narrow, so a
   440px box holds about 175px of unicorn. */
.stage__grid {
  display: grid;
  grid-template-columns: auto minmax(0, 32rem);
  justify-content: center;
  align-items: center;
  /* The pool bleeds 12% past the character each side; the gap has to clear
     that before it starts reading as space between the two. */
  gap: clamp(1.5rem, 5.5vw, 5.5rem);
  max-width: 1180px;
  margin: 0 auto;
}

/* All three crops are square, so the box is too. It is the one element on the
   page with real slack, so it carries most of the size variance. */
.stage__cast {
  position: relative;
  width: clamp(240px, 34vw, 440px);
  aspect-ratio: 1;
  justify-self: center;
  /* Clearance for the droplets that swing below the mass. */
  margin-bottom: clamp(1rem, 3vw, 2rem);
}

/* ---- The pool -----------------------------------------------------------
   The signature: the character stands in a churning mass of its own colour,
   with three droplets orbiting it. Everything here is inside one metaball
   filter, so a droplet swinging back into the mass grows a neck first. */

/* A band across the character's lower half, not a halo around the whole of it
   — the goo is something it is standing in, and a ring of colour behind the
   head would read as a spotlight instead. */
.pool {
  position: absolute;
  left: -12%;
  right: -12%;
  bottom: 1%;
  height: 50%;
  z-index: 0;
  pointer-events: none;
  opacity: 0.82;
  filter: url(#gooPool);
}

/* Alpha stays flat across the body of the mass so the metaball ramp has a
   surface to threshold; only the rim falls off. The churn turns lopsided radii
   against an uneven squash, so it never settles into a readable ellipse. */
.pool__mass {
  position: absolute;
  left: 23%;
  bottom: 9%;
  width: 54%;
  height: 46%;
  background: radial-gradient(
    ellipse at 44% 42%,
    var(--pool-a) 0%,
    var(--pool-a) 74%,
    rgba(255, 254, 251, 0) 100%
  );
  border-radius: 46% 54% 52% 48% / 62% 58% 42% 38%;
  animation: poolChurn 15s ease-in-out infinite;
  will-change: transform;
}

/* `translate` and `transform` are separate properties, so the slow slide of the
   whole puddle composes with the churn instead of overwriting it. */
@keyframes poolChurn {
  0%, 100% { border-radius: 46% 54% 52% 48% / 62% 58% 42% 38%; transform: scale(1, 1);       translate: -3% 0; }
  33%      { border-radius: 58% 42% 44% 56% / 54% 66% 34% 46%; transform: scale(1.07, 0.92); translate: 4% 0; }
  66%      { border-radius: 40% 60% 61% 39% / 68% 50% 50% 32%; transform: scale(0.95, 1.06); translate: -1% 0; }
}

/* Each droplet runs a wide, flat ellipse centred on the mass — far enough out
   at the sides to push a pseudopod through the surface, shallow enough top and
   bottom that the puddle never grows into a halo.

   A circular orbit did neither: the droplets either stayed buried in the mass
   or swung down out of the figure. Radii are percentages of each droplet's own
   size, so the whole thing scales with the character. */
.pool__drop {
  position: absolute;
  aspect-ratio: 1;
  background: radial-gradient(
    circle at 38% 34%,
    var(--pool-a) 0%,
    var(--pool-a) 74%,
    rgba(255, 254, 251, 0) 100%
  );
  border-radius: 38% 62% 64% 36% / 58% 41% 59% 42%;
  animation:
    dropChurn 11s ease-in-out infinite,
    dropOrbit var(--orbit, 21s) linear infinite;
  animation-delay: var(--delay, 0s);
  will-change: translate;
}

.pool__drop--1 {
  --rx: 205%;
  --ry: 48%;
  --orbit: 19s;
  --delay: -3s;
  left: 43.4%;
  top: 52%;
  width: 13%;
}

/* The odd one out, in the group's second hue, running the other way. */
.pool__drop--2 {
  --rx: 200%;
  --ry: 72%;
  --orbit: 26s;
  --delay: -7s;
  left: 44.5%;
  top: 54.5%;
  width: 11%;
  animation-direction: normal, reverse;
  background: radial-gradient(
    circle at 38% 34%,
    var(--pool-b) 0%,
    var(--pool-b) 74%,
    rgba(255, 254, 251, 0) 100%
  );
}

.pool__drop--3 {
  --rx: 320%;
  --ry: 54%;
  --orbit: 33s;
  --delay: -11s;
  left: 45.5%;
  top: 57%;
  width: 9%;
}

@keyframes dropOrbit {
  0%, 100% { translate: var(--rx) 0; }
  12.5%    { translate: calc(var(--rx) * 0.707) calc(var(--ry) * 0.707); }
  25%      { translate: 0 var(--ry); }
  37.5%    { translate: calc(var(--rx) * -0.707) calc(var(--ry) * 0.707); }
  50%      { translate: calc(var(--rx) * -1) 0; }
  62.5%    { translate: calc(var(--rx) * -0.707) calc(var(--ry) * -0.707); }
  75%      { translate: 0 calc(var(--ry) * -1); }
  87.5%    { translate: calc(var(--rx) * 0.707) calc(var(--ry) * -0.707); }
}

@keyframes dropChurn {
  0%, 100% { border-radius: 38% 62% 64% 36% / 58% 41% 59% 42%; }
  25%      { border-radius: 63% 37% 39% 61% / 41% 64% 36% 59%; }
  50%      { border-radius: 56% 44% 34% 66% / 67% 37% 63% 33%; }
  75%      { border-radius: 35% 65% 59% 41% / 44% 60% 40% 56%; }
}

/* ---- The character ------------------------------------------------------ */

/* The idle bob lives on the wrapper, so it keeps running underneath the
   sink/rise animations that play on the images when the character changes. */
.cast__float {
  position: absolute;
  inset: 0;
  z-index: 1;
  animation: castBob 7s ease-in-out infinite;
}

@keyframes castBob {
  0%, 100% { transform: translateY(0) rotate(-1.4deg); }
  50%      { transform: translateY(-10px) rotate(1.4deg); }
}

.cast__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: bottom center;
  transform-origin: 50% 92%;
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 0.45s ease, transform 0.45s var(--ease-smooth);
  filter: drop-shadow(0 18px 26px rgba(80, 52, 30, 0.18));
}

#char-unigoo:checked ~ .stage .cast__img[data-char="unigoo"],
#char-dragoo:checked ~ .stage .cast__img[data-char="dragoo"],
#char-honeygoo:checked ~ .stage .cast__img[data-char="honeygoo"] {
  opacity: 1;
  transform: scale(1);
  z-index: 1;
}

/* ---- Changing character -------------------------------------------------
   The one leaving sinks into the goo and the one arriving is pushed back out
   of it a beat later, while the mass takes the hit and springs back. Added by
   js/crew.js; without it the images simply cross-fade. */

.cast__img.is-sinking {
  z-index: 2;
  animation: castSink 0.42s cubic-bezier(0.55, 0, 0.85, 0.4) forwards;
}

.cast__img.is-rising {
  z-index: 3;
  animation: castRise 0.62s var(--ease-out-back) 0.2s backwards;
}

@keyframes castSink {
  0%   { opacity: 1; transform: translateY(0) scale(1, 1); }
  55%  { opacity: 0.5; transform: translateY(14px) scale(1.11, 0.72); }
  100% { opacity: 0; transform: translateY(28px) scale(1.24, 0.44); }
}

@keyframes castRise {
  0%   { opacity: 0; transform: translateY(32px) scale(1.24, 0.46); }
  45%  { opacity: 1; transform: translateY(3px) scale(0.95, 1.09); }
  72%  { opacity: 1; transform: translateY(0) scale(1.03, 0.98); }
  100% { opacity: 1; transform: translateY(0) scale(1, 1); }
}

/* The whole pool takes the hit and springs back, so the mass keeps churning
   underneath instead of restarting its cycle. */
.pool.is-surging {
  animation: poolSurge 0.8s var(--ease-out-back);
  transform-origin: 50% 88%;
}

@keyframes poolSurge {
  0%, 100% { transform: scale(1, 1); }
  32%      { transform: scale(1.16, 0.82); }
  64%      { transform: scale(0.94, 1.08); }
}

@media (prefers-reduced-motion: reduce) {
  .pool__mass,
  .pool__drop,
  .cast__float,
  .cast__img.is-sinking,
  .cast__img.is-rising {
    animation: none;
  }
}

/* ---- The copy column ---------------------------------------------------- */

.stage__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(1.25rem, 3vw, 2rem);
  min-width: 0;
}

/* ---- Tabs ---------------------------------------------------------------
   The switch sits with the copy it changes, not under the picture. Inactive
   tabs are outlines, not pale fills — three washed-out pastel pills read as
   three disabled buttons. The colour each one carries lives in the goo drop on
   its left instead, and only the chosen tab fills in. */

.stage__tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.stage__tab {
  --tab: var(--pink);
  --tab-glow: rgba(219, 176, 250, 0.55);
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  padding: 0.6em 1.25em;
  border-radius: var(--radius-pill);
  font-family: var(--font-display);
  font-weight: 500;
  text-transform: lowercase;
  font-size: 1rem;
  color: var(--ink);
  cursor: pointer;
  background: rgba(255, 254, 251, 0.5);
  box-shadow: inset 0 0 0 1.5px rgba(80, 52, 30, 0.16);
  transition:
    transform 0.4s var(--ease-smooth),
    background 0.4s var(--ease-smooth),
    box-shadow 0.4s var(--ease-smooth);
}

.stage__tab[data-char="dragoo"] { --tab: var(--green); --tab-glow: rgba(149, 212, 108, 0.6); }
.stage__tab[data-char="honeygoo"] { --tab: var(--yellow); --tab-glow: rgba(255, 206, 87, 0.65); }

/* A droplet of the character's own goo, churning like the pool it belongs to. */
.stage__tab-blob {
  width: 0.7em;
  height: 0.7em;
  flex-shrink: 0;
  background: var(--tab);
  border-radius: 38% 62% 64% 36% / 58% 41% 59% 42%;
  animation: dropChurn 6s ease-in-out infinite;
  transition: transform 0.4s var(--ease-out-back);
}

@media (prefers-reduced-motion: reduce) {
  .stage__tab-blob { animation: none; }
}

.stage__tab:hover {
  background: var(--white);
  box-shadow: inset 0 0 0 1.5px rgba(80, 52, 30, 0.32);
}

.stage__tab:hover .stage__tab-blob {
  transform: scale(1.25);
}

.stage__tab:active {
  transform: translateY(0);
}

#char-unigoo:checked ~ .stage .stage__tab[data-char="unigoo"],
#char-dragoo:checked ~ .stage .stage__tab[data-char="dragoo"],
#char-honeygoo:checked ~ .stage .stage__tab[data-char="honeygoo"] {
  background: var(--tab);
  transform: translateY(-2px);
  box-shadow:
    inset 0 0 0 1.5px transparent,
    0 10px 20px -10px var(--tab-glow),
    0 0 0 4px var(--tab-glow);
}

#char-unigoo:checked ~ .stage .stage__tab[data-char="unigoo"] .stage__tab-blob,
#char-dragoo:checked ~ .stage .stage__tab[data-char="dragoo"] .stage__tab-blob,
#char-honeygoo:checked ~ .stage .stage__tab[data-char="honeygoo"] .stage__tab-blob {
  background: rgba(255, 254, 251, 0.85);
}

/* The radios are visually hidden, so each one lends its focus ring to the tab
   that stands in for it. */
#char-unigoo:focus-visible ~ .stage .stage__tab[data-char="unigoo"],
#char-dragoo:focus-visible ~ .stage .stage__tab[data-char="dragoo"],
#char-honeygoo:focus-visible ~ .stage .stage__tab[data-char="honeygoo"] {
  outline: 3px solid var(--green-accent2);
  outline-offset: 3px;
}

/* ---- Panels -------------------------------------------------------------
   All three share one grid cell, so the block is always exactly as tall as the
   longest of them and switching character never nudges the page around
   underneath the pointer. */

.stage__panels {
  position: relative;
  width: 100%;
  display: grid;
}

.panel {
  grid-area: 1 / 1;
  opacity: 0;
  /* Hidden rather than just transparent, so a screen reader is not read all
     three characters' descriptions in a row. `visibility: hidden` still holds
     the cell open, which is the whole point of the grid above. */
  visibility: hidden;
  transition: opacity 0.4s ease, visibility 0.4s ease;
}

#char-unigoo:checked ~ .stage .panel[data-for="unigoo"],
#char-dragoo:checked ~ .stage .panel[data-for="dragoo"],
#char-honeygoo:checked ~ .stage .panel[data-for="honeygoo"] {
  opacity: 1;
  visibility: visible;
  z-index: 1;
}

/* The name was hidden from sight in the column layout — with a row to itself it
   can be the heading it always was. The full stop takes the group's second hue,
   the same colour as the odd droplet in the pool. */
.panel__name {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: lowercase;
  letter-spacing: -0.03em;
  font-size: clamp(2rem, 4.4vw, 3.1rem);
  line-height: 1;
  color: #131313;
  margin: 0 0 clamp(0.75rem, 1.6vw, 1.1rem);
}

.panel__dot {
  color: var(--pool-b);
}

.panel__body {
  margin: 0;
  max-width: 46ch;
  font-size: clamp(0.98rem, 0.9vw + 0.6rem, 1.08rem);
  line-height: 1.8;
  color: var(--ink);
}

/* The fact is the character telling you something, not another paragraph about
   them — so it gets to be its own object: a bubble of the character's own goo
   that has settled under the copy, with one of the pool's droplets floating in
   it. The label leads in Fredoka; the fact itself stays in the body face. */
.fact {
  display: flex;
  align-items: flex-start;
  gap: 0.85em;
  margin: clamp(1.25rem, 3vw, 1.85rem) 0 0;
  padding: 1.05em 1.5em 1.15em 1.25em;
  max-width: 44ch;
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--pool-a) 32%, var(--cream));
  box-shadow:
    inset 0 0 0 1.5px color-mix(in srgb, var(--pool-a) 60%, transparent),
    0 16px 30px -18px var(--group-tint);
}

/* The second hue, like the odd droplet out in the pool. */
.fact__drop {
  width: 0.95em;
  height: 0.95em;
  margin-top: 0.35em;
  flex-shrink: 0;
  background: var(--pool-b);
  border-radius: 38% 62% 64% 36% / 58% 41% 59% 42%;
  animation: dropChurn 7s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .fact__drop { animation: none; }
}

.fact__text {
  font-size: 0.95rem;
  line-height: 1.7;
  color: var(--ink);
}

.fact__label {
  display: block;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.9rem;
  letter-spacing: 0.02em;
  color: var(--group-ink);
}

/* ==========================================================================
   Act two — the ask

   The ask on the left, what you would be buying on the right. It is the one
   boxed thing on a page that otherwise puts everything straight onto the
   cream, and that is the point: the panel is `.fact` at full size — same
   tinted goo surface, same hairline ring — so the box marks the one place the
   page wants something from you.

   On the right, the first three samples off the line, standing in one.
   ========================================================================== */

.presale {
  position: relative;
  z-index: 2;
  padding: 0 var(--space-3) var(--act-y);
}

/* An even split. The copy used to hold the wider track, because back when the
   photos lived in a frame their height followed their width and the card paid
   twice over for every pixel they took — so the side that was not the point of
   the section was the side made to give ground.

   The toys are capped by `max-width` instead, so their height no longer tracks
   the column at all, and there is nothing left for an uneven split to buy. */
.presale__panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  /* Rows and columns are not the same distance here: the column gap separates
     two halves of an argument, the row gap only lifts the disclosure off the
     hairline under them. The shorthand's single value made that footer sit a
     column-gap away from everything, which was most of what made the card
     feel oversized. */
  row-gap: clamp(1.1rem, 2.2vw, 1.6rem);
  /* Named rather than inlined: the line-up measures its own offset against it,
     and the two drifting apart is what the offset exists to prevent. */
  --panel-gap: clamp(2rem, 5vw, 4.5rem);
  column-gap: var(--panel-gap);
  max-width: 1180px;
  margin: 0 auto;
  padding: clamp(1.5rem, 2.9vw, 2.6rem);
  border-radius: var(--radius-lg);
  /* The white light behind the toys is wider than the column it sits in — at
     narrow widths wider than the card. Clipped to the card's own corners so
     the light stays inside it instead of spilling onto the page. `clip`
     rather than `hidden`: no scroll container, and the panel's outer shadow
     is untouched either way. */
  overflow: clip;
  /* Lighter than the `.fact` bubble it is modelled on, because it covers about
     forty times the area: the tint that reads as a highlight at bubble size
     reads as a slab of colour at this one. */
  background: color-mix(in srgb, var(--pool-a) 18%, var(--cream));
  box-shadow:
    inset 0 0 0 1.5px color-mix(in srgb, var(--pool-a) 45%, transparent),
    0 34px 60px -44px var(--group-tint);
}

.presale__copy {
  min-width: 0;
}

.presale__eyebrow {
  font-family: var(--font-display);
  font-weight: 500;
  text-transform: lowercase;
  letter-spacing: 0.14em;
  font-size: 0.85rem;
  color: var(--group-ink);
  margin: 0 0 var(--space-1);
}

.presale__title {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: lowercase;
  letter-spacing: -0.03em;
  font-size: clamp(1.9rem, 3.6vw, 2.85rem);
  line-height: 1.05;
  color: #131313;
  margin: 0;
}

.presale__body {
  margin: clamp(0.9rem, 2vw, 1.3rem) 0 0;
  max-width: 42ch;
  font-size: clamp(0.98rem, 0.9vw + 0.6rem, 1.06rem);
  line-height: 1.8;
  color: var(--ink);
}

/* The last thing in the copy column now that the note under it is gone, so it
   carries a little room of its own: a filled pill sitting a row-gap off the
   hairline — or off the toys' heads, stacked — reads as cramped in a way a
   line of small type never did. */
.presale__actions {
  margin: clamp(1.35rem, 3vw, 1.9rem) 0 clamp(0.4rem, 1vw, 0.7rem);
}

.presale__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.9em 1.6em;
  border-radius: var(--radius-pill);
  background: var(--ink);
  color: var(--cream);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(0.92rem, 1.1vw, 1rem);
  line-height: 1.3;
  text-decoration: none;
  box-shadow: 0 14px 28px -14px rgba(80, 52, 30, 0.55);
  transition:
    transform 0.35s var(--ease-smooth),
    background 0.35s var(--ease-smooth),
    box-shadow 0.35s var(--ease-smooth);
}

.presale__cta svg {
  width: 1.05em;
  height: 1.05em;
  transition: transform 0.35s var(--ease-smooth);
}

.presale__cta:hover,
.presale__cta:focus-visible {
  transform: translateY(-2px);
  background: var(--green-accent2);
  box-shadow: 0 18px 32px -14px rgba(89, 127, 47, 0.55);
}

.presale__cta:hover svg,
.presale__cta:focus-visible svg {
  transform: translateX(4px);
}

.presale__cta:active {
  transform: translateY(0);
}

/* ---- The disclosure -----------------------------------------------------
   Stacked under the photos it qualified, this ran to three lines in a
   half-width column and made the card taller than anything in it justified.
   Across the foot of the panel it is one line, and the row it adds is shorter
   than the two lines it gives back.

   Full width also puts it under both halves at once, which is the more honest
   place for it: it qualifies the photos on the right and the offer on the
   left. The hairline is what makes that read as a deliberate footer band
   rather than a sentence that fell off the bottom of the card. */
.presale__disclosure {
  grid-column: 1 / -1;
  margin: 0;
  padding-top: clamp(0.9rem, 2vw, 1.25rem);
  border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  font-size: 0.75rem;
  line-height: 1.6;
  /* 80% clears 4.5:1 against the panel's tint with room to spare; 72% cleared
     it by 0.12 and only against the palest of the three character themes. */
  color: color-mix(in srgb, var(--ink) 80%, transparent);
}

/* ---- The line-up --------------------------------------------------------
   The three samples, standing in a line across the middle of the card. This
   replaced a carousel, and the reasons are worth keeping written down, because
   each one was a real attempt at the same job:

     1. One toy at a time in a 5:4 frame. The frame's height followed its width
        and it was the tallest thing in the panel, so every pixel the photos
        took in width the card paid for twice in height.
     2. Each toy lit from behind by its own group colour, so the frame supplied
        the room a cut-out does not come with. At frame size that stopped
        reading as light and started reading as a halo round a plush toy.
     3. A caption in the top-left corner of the frame, because a frame that
        size has nowhere else to put one, and four dots and two arrows under it
        to explain a control that only existed because the frame held one photo.

   Three toys side by side need none of it. You see all of them at once, they
   were introduced by name two acts above, and the heading over them already
   says what they are doing — so the frames, the coloured light, the captions
   and the controls all go, and what is left is the photographs.

   The two alignments here are doing opposite jobs and both are needed:
   `align-items: end` inside the row keeps the three toys' feet level with each
   other, which is what makes them read as one group standing together rather
   than three photographs pasted at three heights. `align-self: center` then
   floats that whole group in the middle of the card's row. An earlier pass
   stood them on the disclosure's hairline instead — `align-self: end` with the
   row gap at zero — which grounded them but left a band of empty panel above
   their heads. */
.lineup {
  /* Not flex: three photos with three aspect ratios, sized by width in `fr`,
     come out at the heights below — matching their widths instead would stand
     a short bee beside a tall unicorn. The tracks are each toy's width at a
     shared height, so `fr` does the arithmetic and the row scales with its
     column instead of overflowing it at some middle width. */
  display: grid;
  grid-template-columns: 0.678fr 0.596fr 0.741fr;
  align-items: end;
  align-self: center;
  justify-items: center;
  gap: clamp(0.45rem, 1.5vw, 1rem);
  width: 100%;
  /* Wider than this and the toys start rivalling the character on the stage
     above, which is the one that is meant to be looked at. */
  max-width: 500px;
  margin-inline: auto;
  min-width: 0;
  position: relative;
  /* Centred on its track, but the track is not the space the eye is measuring:
     the column gap to its left is wider than the panel's padding to its right,
     so a group centred on the track reads as leaning against the right wall.
     Half the gap comes off that side and the trio sits in the middle of the
     room it actually looks like it is standing in. */
  translate: calc(var(--panel-gap, 0px) / -2);
}

/* The light the photographs were taken in, put back behind them.

   An earlier pass lit each toy from behind in its own group colour, which at
   that size stopped reading as light and started reading as a halo. White at
   the size of the whole group is a different thing: it is the seamless the
   three are standing on, so the cut-outs have somewhere to be rather than
   floating on a tinted panel. Soft all the way out — a hard edge anywhere on
   it turns the light back into a shape. */
.lineup::before {
  content: "";
  position: absolute;
  z-index: 0;
  left: 50%;
  top: 50%;
  width: 134%;
  height: 172%;
  translate: -50% -50%;
  background: radial-gradient(
    closest-side,
    #fff 0%,
    rgba(255, 255, 255, 0.86) 34%,
    rgba(255, 255, 255, 0.42) 63%,
    rgba(255, 255, 255, 0) 100%
  );
  pointer-events: none;
}

/* Above the light rather than under it. Stacked with `z-index` rather than
   sending the wash to `-1`, which would drop it behind the panel's own tinted
   background instead of sitting on it. */
.lineup__slot {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  min-width: 0;
}

/* They arrive left to right, the way a queue fills up. */
.lineup__slot:nth-child(2) { --reveal-delay: 0.09s; }
.lineup__slot:nth-child(3) { --reveal-delay: 0.18s; }

/* Two shadows, both brown and both cast downward: a short one at the feet for
   contact, a long soft one for the room. Nothing at zero offset — a shadow
   with no offset is a glow, and the glow is what made a shelf of plush toys
   read as a séance. */
.lineup__toy {
  display: block;
  width: 100%;
  height: auto;
  /* The three were shot under soft studio light on white, which is kind to the
     fabric and unkind to the colour — straight off the card the unicorn reads
     grey and the dragon reads sage. The grade goes ahead of the shadows in the
     chain so it lifts the toys and leaves the brown cast underneath alone.
     Saturation does most of the work; brightness and contrast are along to keep
     the lift from flattening, and all three stay low enough that the plush
     still looks like cloth rather than plastic. */
  filter:
    saturate(1.18)
    contrast(1.06)
    brightness(1.05)
    drop-shadow(0 4px 4px rgba(80, 52, 30, 0.18))
    drop-shadow(0 13px 15px rgba(80, 52, 30, 0.14));
  animation: toySway var(--sway, 11s) ease-in-out infinite;
  animation-delay: var(--sway-delay, 0s);
}

/* The tracks above are each toy's true width at its true height, read off the
   group shot the three were photographed in: the bee and the dragon stand
   about level and the unicorn is a little shorter. Cropped to one height they
   would line up like a product grid; a centimetre between them reads as three
   toys that happen to be standing together. */
.lineup__toy--honeygoo {
  --sway: 12.5s;
}

.lineup__toy--dragoo {
  --sway: 10s;
  --sway-delay: -3.5s;
}

.lineup__toy--unigoo {
  --sway-delay: -6s;
}

/* Barely there, and slower than anything else on the page: enough that three
   toys standing still do not read as a product shot, not enough to pull the
   eye off the button beside them. */
@keyframes toySway {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}

@media (prefers-reduced-motion: reduce) {
  .lineup__toy {
    animation: none;
  }
}

/* ==========================================================================
   Act three — what makes the goo crew special

   Two columns of points, with the heading sitting in the first cell of the
   grid rather than holding a column of its own. Before this it held one half
   of the row and a single stack of seven held the other, which cost the
   points twice: every sentence broke across two lines on the narrow track,
   and the drop beside each one had to stay small enough that seven of them
   did not run the height of the screen.

   Across the full row in two columns the sentences fit on one or two lines
   and the drops can be what they are everywhere else on this page — goo, at a
   size you can actually see it move. Seven is odd, so two-across would leave
   an orphan in the last row; the heading takes the eighth slot and the act
   closes on a full row.

   Still no cards, no rules and nothing to hover open.
   ========================================================================== */

.made {
  position: relative;
  z-index: 2;
  padding: 0 var(--space-3) var(--act-y);
}

/* The points are grid items of this, not of the list — `.made__grid` is
   `display: contents` — which is what lets the heading sit in the same flow
   and fill the odd slot. */
.made__inner {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: center;
  column-gap: clamp(2rem, 4vw, 3.75rem);
  /* Rows are further apart than the two lines inside a point, or a wrapped
     sentence sits closer to the point under it than to its own first line. */
  row-gap: clamp(1.75rem, 3.2vw, 2.75rem);
  max-width: 1180px;
  margin: 0 auto;
}

.made__title {
  margin: 0;
  /* Held under the width of its cell: the heading is the one thing on the row
     with no drop beside it, and a measure keeps it a block rather than three
     lines trailing off toward the point it sits next to. */
  max-width: 15ch;
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: lowercase;
  letter-spacing: -0.03em;
  font-size: clamp(1.75rem, 3.2vw, 2.75rem);
  line-height: 1.08;
  color: #131313;
}

.made__grid {
  display: contents;
}

/* The goo, then the sentence, side by side and centred against each other.
   The drop gets a fixed track rather than sizing it — no two drops are quite
   the same size, and on `auto` that would land the sentences in the two
   columns on different left edges. */
.made__item {
  --drop: clamp(66px, 6.6vw, 92px);
  display: grid;
  grid-template-columns: var(--drop) minmax(0, 1fr);
  align-items: center;
  column-gap: clamp(1rem, 1.7vw, 1.5rem);
}

/* By row, not by point: two land at once now, and stepping them one at a time
   would run the last delay past the moment the reader reaches it. */
.made__item:nth-child(2),
.made__item:nth-child(3) { --reveal-delay: 0.08s; }
.made__item:nth-child(4),
.made__item:nth-child(5) { --reveal-delay: 0.16s; }
.made__item:nth-child(6),
.made__item:nth-child(7) { --reveal-delay: 0.24s; }

/* A drop of the active character's goo with the icon resting in it, morphing
   slowly and out of phase with its neighbours.

   Lit rather than filled: a highlight where light would catch a wet surface,
   the group's second hue pooling at the far edge. At the size these used to
   be a flat tint was the honest choice — there was no room for a fill to
   read. At this size a flat tint is the thing that looks unfinished. */
.made__blob {
  justify-self: center;
  display: grid;
  place-items: center;
  width: calc(var(--drop) * var(--drop-scale, 1));
  height: calc(var(--drop) * var(--drop-scale, 1));
  background:
    radial-gradient(
      118% 118% at 32% 24%,
      color-mix(in srgb, var(--pool-a) 52%, #fff) 0%,
      color-mix(in srgb, var(--pool-a) 88%, var(--cream)) 46%,
      color-mix(in srgb, var(--pool-b) 42%, var(--pool-a)) 100%
    );
  border-radius: 58% 42% 46% 54% / 52% 58% 42% 48%;
  /* Tinted with the group's own glow and offset down, so a drop sits on the
     page instead of hovering over it. */
  box-shadow: 0 14px 26px -16px var(--group-tint);
  animation: blobMorph var(--morph, 14s) ease-in-out infinite;
  animation-delay: var(--morph-delay, 0s);
  transition:
    transform 0.5s var(--ease-out-back),
    box-shadow 0.5s var(--ease-smooth);
}

/* Each drop keeps its own period, phase and size. The sizes sit in a narrow
   band on purpose — enough that no two beside each other match, not so much
   that one reads as a mistake. */
.made__item:nth-child(1) .made__blob { --morph: 14s; --morph-delay:   0s; --drop-scale: 1;    }
.made__item:nth-child(2) .made__blob { --morph: 17s; --morph-delay:  -3s; --drop-scale: 0.93; }
.made__item:nth-child(3) .made__blob { --morph: 12s; --morph-delay:  -7s; --drop-scale: 0.97; }
.made__item:nth-child(4) .made__blob { --morph: 19s; --morph-delay:  -5s; --drop-scale: 0.91; }
.made__item:nth-child(5) .made__blob { --morph: 15s; --morph-delay: -11s; --drop-scale: 1;    }
.made__item:nth-child(6) .made__blob { --morph: 13s; --morph-delay:  -9s; --drop-scale: 0.95; }
.made__item:nth-child(7) .made__blob { --morph: 16s; --morph-delay:  -2s; --drop-scale: 0.9;  }

/* Wider swings than the drops used to take: the old amplitude was tuned to
   read at 48px, and at twice that it barely leaves a circle. */
@keyframes blobMorph {
  0%, 100% { border-radius: 58% 42% 46% 54% / 52% 58% 42% 48%; }
  33%      { border-radius: 40% 60% 63% 37% / 64% 42% 58% 36%; }
  66%      { border-radius: 55% 45% 35% 65% / 38% 63% 42% 57%; }
}

@media (prefers-reduced-motion: reduce) {
  .made__blob { animation: none; }
}

/* Ink rather than the group's accent: on the green group's goo the accent is
   too close in value to read as a shape. */
.made__blob svg {
  width: 40%;
  height: 40%;
  color: var(--ink);
}

.made__item:hover .made__blob {
  transform: scale(1.06) rotate(-3deg);
  box-shadow:
    0 14px 26px -16px var(--group-tint),
    0 0 0 6px var(--group-tint);
}

/* The only text in the point, so it sets a step up from the body size used
   inside the acts above — there is no name over it to carry the eye down the
   list. No width cap of its own: the column is already a readable measure,
   and a cap here would only add an empty gutter on top. */
.made__text {
  margin: 0;
  font-size: clamp(0.98rem, 1.02vw, 1.05rem);
  line-height: 1.65;
  /* Most of the seven run past one line. Left alone they break with a single
     word under a full line, which at this size reads as a mistake rather than
     a wrap; `pretty` pulls a few words down to balance the two. */
  text-wrap: pretty;
  color: var(--ink);
  opacity: 0.86;
}

/* ==========================================================================
   Narrower screens

   Both two-ups become one column, and the six drops go to two and then one.
   ========================================================================== */

@media (max-width: 960px) {
  .stage__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(1.5rem, 4vw, 2.5rem);
    justify-items: center;
  }

  .stage__copy {
    align-items: center;
    text-align: center;
  }

  .stage__tabs {
    justify-content: center;
  }

  .panel__body {
    margin-inline: auto;
  }

  .fact {
    margin-inline: auto;
    text-align: left;
  }

  /* The ask stacks over the photos rather than beside them — it is the reason
     the section exists, so it keeps the top of the panel.

     Stacked, the copy and the toys need the full gap between them, but the
     disclosure still only needs lifting off its hairline — so the extra
     distance goes onto the copy block rather than onto every row. */
  .presale__panel {
    grid-template-columns: minmax(0, 1fr);
  }

  .presale__copy {
    margin-bottom: clamp(0.75rem, 2.8vw, 1.15rem);
  }

  .presale__body {
    max-width: 52ch;
  }

  /* Left, not centred: everything above it in the stacked panel starts at the
     panel's left edge, and a centred row of toys under a left-aligned column
     gives the block two axes and no alignment.

     No offset either — stacked there is no column gap on one side and padding
     on the other for the trio to sit unevenly between. */
  .lineup {
    margin-inline: 0;
    translate: none;
  }

  /* Back to one column of points, with the heading over them rather than
     tucked into the grid — at this width a two-across row leaves the
     sentences on a track too narrow to hold more than four words a line. */
  .made__inner {
    grid-template-columns: minmax(0, 1fr);
    /* Tighter than the two-column rhythm: stacked, most points fit on one
       line, and a gap sized for two-line rows leaves the stack all air. */
    row-gap: clamp(1.1rem, 2.4vw, 1.6rem);
  }

  .made__title {
    max-width: 20ch;
    /* Stacked, the row gap is all that separates the heading from the first
       point, and a gap tuned to sit between two points is too tight to read
       as the break between the claim and its evidence. */
    margin-bottom: clamp(0.6rem, 2.2vw, 1.4rem);
  }
}

@media (max-width: 620px) {
  .stage__cast {
    width: min(78vw, 300px);
  }

  /* Three tabs on one row at this width, or they wrap two-and-one and the
     switch stops reading as one control. */
  .stage__tabs {
    gap: 0.4rem;
  }

  .stage__tab {
    font-size: 0.88rem;
    padding: 0.55em 0.95em;
  }

  /* The panel loses its inset and sits edge to edge with the rest of the page,
     so the frame inside it stays as wide as the copy above it. */
  .presale__panel {
    padding: clamp(1.25rem, 5vw, 1.75rem);
    border-radius: var(--radius-md);
  }

  /* The goo stays beside its sentence rather than stacking over it: at this
     width every point wraps to three lines, and putting a drop above each one
     would make the act twice the height of the screen. */
  .made__inner {
    row-gap: 1.5rem;
  }

  .made__item {
    --drop: 54px;
    /* Against the first line rather than the middle of three: centred, the
       drop floats away from the start of the sentence it belongs to. */
    align-items: start;
    column-gap: 0.9rem;
  }

  .made__text {
    font-size: 0.94rem;
  }
}
