.hero {
  padding: 30px 0 0;
  position: relative;
  z-index: 2;
}
/* On phones, give the hero a small bottom buffer so an absolute-positioned
 * buddy banner that wraps onto a second line doesn't bleed into the
 * ticker sitting immediately below the hero stage. */
@media (max-width: 720px) { .hero { padding: 16px 0 24px; } }

.hero__stage {
  /* Explicit pixel-definite width AND height. We avoid `aspect-ratio` on
   * the stage so descendants never have to resolve against an
   * aspect-ratio-derived parent size — iOS Safari has long-standing
   * bugs where percentage / aspect-ratio chains drop a step and a
   * descendant ends up too short. Children read --stage-w / --stage-h
   * via calc() to get definite pixel dimensions every time. */
  --stage-w: min(820px, 92vw);
  --stage-h: calc(var(--stage-w) * 0.85);
  position: relative;
  width: var(--stage-w);
  height: var(--stage-h);
  margin: 0 auto;
}

.hero__halo {
  position: absolute;
  inset: -8%;
  background: radial-gradient(circle at 50% 55%, var(--p3) 0%, transparent 55%);
  opacity: .42;
  filter: blur(60px);
  z-index: 0;
  transition: background var(--fade) var(--ease), opacity var(--fade) var(--ease);
  animation: hero-halo-breath 14s ease-in-out infinite;
}
@keyframes hero-halo-breath {
  0%, 100% { transform: scale(1);   opacity: .42; }
  50%      { transform: scale(1.07); opacity: .6; }
}

/* Central badge — masked SVG painted with the palette's secondary accent
   so the brand mark visibly shifts color through each palette. */
.hero__badge {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 46%;
  max-width: 380px;
  aspect-ratio: 1 / 1;
  z-index: 2;
  background: var(--p2);
  -webkit-mask: url("/assets/svg/rbco-black-circle.svg") no-repeat center / contain;
          mask: url("/assets/svg/rbco-black-circle.svg") no-repeat center / contain;
  transition: background-color var(--fade) var(--ease);
}

/* Buddies orbit gently around the badge. They are stacked in DOM order;
 * only the active group is faded in (see /assets/js/buddy-cycle.js).
 * Outer motion is opt-in via the `animate` column in the sheet:
 *   - float  → gentle drift loop (default for image buddies)
 *   - jiggle → tighter wiggle in place
 *   - none   → no outer motion (default for HTML buddies that animate themselves)
 */
.hero__buddy {
  position: absolute;
  transform-origin: 50% 50%;
  pointer-events: none;
  filter: drop-shadow(0 14px 22px rgba(0, 0, 0, .18));
  opacity: 0;
  transition: opacity 1.1s ease;
}
.hero__buddy.is-visible { opacity: 1; }
.hero__buddy--front { z-index: 4; }
.hero__buddy--under { z-index: 1; }

.hero__buddy--anim-float {
  animation: buddy-float 9s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * -1.7s);
}
.hero__buddy--under.hero__buddy--anim-float { animation-duration: 12s; }

.hero__buddy--anim-jiggle {
  animation: buddy-jiggle 2.6s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * -.7s);
}

.hero__buddy--anim-none {
  /* Static placement — let the buddy (e.g. an HTML scene) animate itself.
   * scale() applies on top of the centering translate so the bounding
   * box stays anchored to the configured (x, y) point. */
  transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) scale(var(--scale, 1));
}

/* HTML scene buddies — now a plain div containing an inline scene
 * (see scenes.css). Width and height are computed in calc() against
 * the stage's pixel-definite --stage-w / --stage-h, so the div gets
 * a fully resolved size with no aspect-ratio chain anywhere. The
 * per-buddy --w-pct / --h-pct (just numbers, no units) come in as
 * inline custom properties from hero.php. Default height is 1:1
 * (matches width); the .--explicit-height modifier swaps in --h-pct
 * when the sheet specifies it. The drop-shadow filter is dropped on
 * scene wrappers — the inner PNG parts have their own painted edges,
 * and a filter on the wrapper would force a full repaint each frame
 * of the limb animations. */
.hero__buddy--html {
  filter: none;
  width:  calc(var(--w-pct, 25) * var(--stage-w) / 100);
  height: calc(var(--w-pct, 25) * var(--stage-w) / 100);
}
.hero__buddy--html.hero__buddy--explicit-height {
  height: calc(var(--h-pct, 25) * var(--stage-h) / 100);
}

/* Lively caption that follows the active buddy group. Sits just below the
 * central badge so it reads as a speech bubble for the buddy on stage. */
.hero__buddy-banner {
  position: absolute;
  left: 50%;
  top: 78%;
  transform: translate(-50%, 0) rotate(-2deg);
  margin: 0;
  z-index: 5;
  font-family: var(--hand);
  font-weight: 600;
  /* Per-group banner_size column scales the base size — set inline via
   * --banner-size by buddy-cycle.js. Default 1.0 keeps prior behavior. */
  font-size: calc(clamp(20px, 2.4vw, 30px) * var(--banner-size, 1));
  color: var(--p1);
  text-align: center;
  white-space: nowrap;
  /* Cap the width so a long banner can't run past the stage edges. On
   * narrow screens we also drop `nowrap` so the line wraps instead of
   * scrolling horizontally. */
  max-width: 92%;
  opacity: 0;
  transition: opacity .6s ease, color var(--fade) var(--ease);
  pointer-events: none;
}
.hero__buddy-banner.is-visible { opacity: 1; }
@media (max-width: 720px) {
  .hero__buddy-banner {
    white-space: normal;
    line-height: 1.04;
    /* Smaller cap so 2-3 line banners stay inside the stage and don't
     * cross into the ticker that sits directly below. */
    font-size: calc(clamp(15px, 4.2vw, 19px) * var(--banner-size, 1));
    max-width: 86%;
    /* Anchor a bit higher on small screens so a 2-line banner clears the
     * 30px gap between the hero and the ticker. */
    top: 74%;
  }
}

/* Buddy keyframes thread the operator's `--scale` through every step so
 * scale is honored regardless of which animation variant is active. */
@keyframes buddy-float {
  0%, 100% {
    transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) scale(var(--scale, 1));
  }
  25% {
    transform: translate(calc(-50% + 10px), calc(-50% - 8px)) rotate(calc(var(--rot, 0deg) + 3deg)) scale(var(--scale, 1));
  }
  50% {
    transform: translate(calc(-50% - 4px), calc(-50% + 6px)) rotate(calc(var(--rot, 0deg) - 2deg)) scale(var(--scale, 1));
  }
  75% {
    transform: translate(calc(-50% + 6px), calc(-50% - 4px)) rotate(calc(var(--rot, 0deg) + 4deg)) scale(var(--scale, 1));
  }
}

/* Tight, in-place wiggle for buddies that should feel busy but not drift. */
@keyframes buddy-jiggle {
  0%, 100% { transform: translate(-50%, -50%) rotate(calc(var(--rot, 0deg) - 1.5deg)) scale(var(--scale, 1)); }
  50%      { transform: translate(-50%, -50%) rotate(calc(var(--rot, 0deg) + 1.5deg)) scale(var(--scale, 1)); }
}

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