/* ==========================================================================
   HERO CAROUSEL
   Full-bleed, admin-controlled, separate mobile creatives.

   The hero image is the LCP element on the homepage, so slide 1 is eager and
   preloaded while every other slide is lazy. Nothing here animates layout -
   only transform and opacity.
   ========================================================================== */

/* ---- Height -------------------------------------------------------------
   Driven by ASPECT RATIO, not by viewport height.

   It used to be clamp(460px, 78dvh, 620px) on phones and up to 780px on
   desktop. Two things went wrong with that:

     1. It ate the screen. At 78dvh a phone showed the banner and nothing else,
        so the first scroll revealed no gifts — the catalogue looked empty.

     2. Worse, it cropped the artwork. A viewport-height box has whatever
        proportion the visitor's screen happens to have, and object-fit:cover
        then trims the sides to fill it. On a banner with the headline baked
        into the image, that cuts the words off — which is exactly what
        "A THREAD OF LOVE" losing its left edge was.

   Matching the container to the ratio the artwork was designed at fixes both.
   Supply banners at:

     Desktop   2400 x 1000   (2.4:1)
     Mobile    1080 x 1350   (4:5)

   The mobile creative matters. Without one, the landscape desktop image is
   squeezed into a portrait box and cropped hard. --------------------------- */

.hero {
  position: relative;
  overflow: hidden;
  background: var(--tbs-green-deep);

  /* Full bleed, stated explicitly. See the note below for why this matters. */
  width: 100%;

  /* Height derived from viewport WIDTH, not from aspect-ratio.

     aspect-ratio looks like the right tool here and is a trap. Once max-height
     clamps the height, the ratio has to give somewhere — and the browser
     satisfies it by shrinking the WIDTH instead, which is what left a white
     strip down the right-hand side of the banner.

     min(vw, px) has no such ambiguity: width always fills, and the height is
     whichever of the two is smaller.

       41.67vw = width ÷ 2.4, the desktop creative's ratio
       125vw   = width × 1.25, the 1080x1350 mobile creative's ratio          */
  height: min(125vw, 68dvh);
  min-height: 380px;
}

@media (min-width: 768px) {
  .hero {
    height: min(41.67vw, 560px);
    min-height: 340px;
  }
}

@media (min-width: 1600px) {
  /* Past this width the ratio alone would grow the banner beyond what a hero
     should occupy on a large monitor. */
  .hero { height: min(41.67vw, 620px); }
}

/* ---- Slides ---------------------------------------------------------- */
/* Three transition styles, chosen per site in Settings → Motion.
   Fade  - quiet, the safest default over photography
   Slide - the modern editorial feel: the new slide pushes in from the right
   Zoom  - the new slide grows through the outgoing one                        */

.hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 900ms var(--ease-brand), visibility 0s linear 900ms;
}

.hero__slide.is-on {
  opacity: 1;
  visibility: visible;
  transition: opacity 900ms var(--ease-brand), visibility 0s;
  z-index: 1;
}

/* ---- Slide ----------------------------------------------------------- */
/* A real slide: the incoming banner travels in from the right and covers the
   one before it. This replaced a clip-path reveal, which held the photograph
   still and opened a frame across it — technically elegant, but not what
   anyone means by "slide".

   The trick is in the outgoing slide's transition. It is given a 0s transform
   with an 800ms DELAY, so it holds its position for the whole animation and
   only snaps aside once the incoming one has finished covering it. Animate it
   out instead and it drifts right, opening a gap on the left that the incoming
   slide has not reached yet — background showing through mid-transition is
   exactly what makes a carousel look cheap.

   translate only: no scaling, so nothing is ever cropped. That was the
   complaint against the zoom style. */

.hero--slide .hero__slide {
  opacity: 1;
  visibility: hidden;
  transform: translateX(100%);
  transition: transform 0s linear 800ms, visibility 0s linear 800ms;
  pointer-events: none;
}

.hero--slide .hero__slide.is-on {
  visibility: visible;
  transform: translateX(0);
  transition: transform 800ms var(--ease-brand);
  pointer-events: auto;
  z-index: 2;
}

/* Going backwards, it arrives from the other side. */
.hero--slide.is-reverse .hero__slide { transform: translateX(-100%); }
.hero--slide.is-reverse .hero__slide.is-on { transform: translateX(0); }

/* The copy travels a little further on a slide, so it feels carried in. */
.hero--slide .hero__slide .hero__eyebrow,
.hero--slide .hero__slide .hero__title,
.hero--slide .hero__slide .hero__sub,
.hero--slide .hero__slide .hero__cta { transform: translateX(40px); }

.hero--slide .hero__slide.is-on .hero__eyebrow,
.hero--slide .hero__slide.is-on .hero__title,
.hero--slide .hero__slide.is-on .hero__sub,
.hero--slide .hero__slide.is-on .hero__cta { transform: none; }

/* ---- Zoom ------------------------------------------------------------ */

.hero--zoom .hero__slide {
  transform: scale(1.08);
  transition: opacity 800ms var(--ease-brand),
              transform 1100ms var(--ease-brand),
              visibility 0s linear 800ms;
}

.hero--zoom .hero__slide.is-on {
  transform: scale(1);
  transition: opacity 800ms var(--ease-brand),
              transform 1100ms var(--ease-brand),
              visibility 0s;
}

/* The Ken Burns drift would fight the zoom transition, so it is dropped here. */
.hero--zoom .hero__slide.is-on .hero__media img { animation: none; }

.hero__media { position: absolute; inset: 0; }

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Ken Burns: a very slow drift on the active slide only. Subtle enough that it
   reads as depth rather than movement. */
.hero__slide.is-on .hero__media img {
  animation: drift 18s var(--ease-brand) forwards;
}

@keyframes drift {
  from { transform: scale(1.02) translate3d(0, 0, 0); }
  to   { transform: scale(1.11) translate3d(-1.2%, -1%, 0); }
}

.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, #00301F 0%, rgba(0, 48, 31, .45) 62%, rgba(0, 48, 31, .18) 100%);
  z-index: 1;
}

/* ---- Copy ------------------------------------------------------------ */

.hero__body {
  position: relative;
  z-index: 2;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-3);
  max-width: 34ch;
  color: rgba(255, 255, 255, .9);
}

.hero__body[data-align="center"] { margin-inline: auto; text-align: center; align-items: center; }
.hero__body[data-align="right"]  { margin-left: auto; text-align: right; align-items: flex-end; }

.hero__eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--gold);
}

.hero__title {
    font-family: var(--font-display);
    /* 4.6vw, not 6vw. At 6vw the headline peaked at 1280px (76.8px) and was
     actually LARGER on a laptop than on a 2560px monitor, where the 4.5rem cap
     takes over at 72px — biggest exactly where there is least room. 4.6vw
     reaches the cap around 1560px instead, so it grows with the screen. */
    font-size: clamp(2.2rem, 4.6vw, 4.5rem);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    color: var(--white);
    margin: 0;
}

.hero__sub {
  font-size: 1.0625rem;
  color: rgba(255, 255, 255, .85);
  margin: 0;
  max-width: 44ch;
}

.hero__cta { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-3); }

/* Copy rises in when its slide becomes active. Staggered so the eye lands on
   the eyebrow, then the headline, then the button. */
.hero__slide .hero__eyebrow,
.hero__slide .hero__title,
.hero__slide .hero__sub,
.hero__slide .hero__cta {
  opacity: 0;
  transform: translateY(24px);
}

.hero__slide.is-on .hero__eyebrow,
.hero__slide.is-on .hero__title,
.hero__slide.is-on .hero__sub,
.hero__slide.is-on .hero__cta {
  opacity: 1;
  transform: none;
  transition: opacity 700ms var(--ease-brand), transform 700ms var(--ease-brand);
}

.hero__slide.is-on .hero__eyebrow { transition-delay: 180ms; }
.hero__slide.is-on .hero__title   { transition-delay: 260ms; }
.hero__slide.is-on .hero__sub     { transition-delay: 360ms; }
.hero__slide.is-on .hero__cta     { transition-delay: 460ms; }

/* ---- Pagination ------------------------------------------------------ */

.hero__dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--sp-5);
  z-index: 3;
  display: flex;
  gap: var(--sp-3);
  justify-content: center;
  align-items: center;
}

.hero-dot {
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  position: relative;
  display: grid;
  place-items: center;
}

.hero-dot::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .45);
  transition: background var(--dur-micro) var(--ease-brand);
}

.hero-dot:hover::after { background: rgba(255, 255, 255, .8); }
.hero-dot[aria-current="true"]::after { background: var(--gold); }

/* Progress ring that fills over the autoplay interval, so the visitor can see
   how long they have before it moves on. */
.hero-dot__ring {
  position: absolute;
  inset: 0;
  transform: rotate(-90deg);
}

.hero-dot__ring circle {
  fill: none;
  stroke: var(--gold);
  stroke-width: 1.6;
  stroke-dasharray: 82;
  stroke-dashoffset: 82;
  opacity: 0;
}

.hero-dot[aria-current="true"] .hero-dot__ring circle {
  opacity: 1;
  animation: ring-fill var(--hero-interval, 6s) linear forwards;
}

.hero--paused .hero-dot__ring circle { animation-play-state: paused; }

@keyframes ring-fill { to { stroke-dashoffset: 0; } }

/* ---- Arrows (desktop only) ------------------------------------------- */

.hero__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 48px;
  height: 48px;
  display: none;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, .3);
  border-radius: 50%;
  background: rgba(0, 48, 31, .28);
  color: var(--white);
  cursor: pointer;
  backdrop-filter: blur(3px);
}

@media (min-width: 1024px) {
    .hero__arrow {
        display: grid;
    }

    /* The arrows are pinned to the VIEWPORT (24px in, 48px wide) while the copy
     is pinned to the CONTAINER. Between 1024px and ~1400px the container runs
     edge to edge, so both land in the same 24-72px lane and the prev arrow
     sits across the headline — which is why it looks wrong on a 1366px laptop
     and fine on a 2560px monitor.

     Reserve the arrow's lane in the hero's own container, and stop reserving
     it once the container's centring margins are wide enough to hold it
     themselves. 88px = 24px inset + 48px arrow + 16px breathing room. */
    .hero__slide > .container {
        padding-inline: max( var(--sp-5), calc(88px - max(0px, (100vw - var(--container)) / 2)) );
    }
}

.hero__arrow:hover { background: rgba(0, 48, 31, .55); border-color: rgba(255, 255, 255, .6); }
.hero__arrow--prev { left: var(--sp-5); }
.hero__arrow--next { right: var(--sp-5); }

/* ---- Reduced motion -------------------------------------------------- */
/* No drift, no rise, no autoplay ring. The carousel still works; it just stops
   moving on its own. */

@media (prefers-reduced-motion: reduce) {
    .hero__slide.is-on .hero__media img {
        animation: none;
        transform: none;
    }

    .hero__slide .hero__eyebrow,
    .hero__slide .hero__title,
    .hero__slide .hero__sub,
    .hero__slide .hero__cta {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .hero-dot[aria-current="true"] .hero-dot__ring circle {
        animation: none;
        opacity: 0;
    }

    /* Every style collapses to a plain instant swap. */
    .hero__slide,
    .hero--slide .hero__slide,
    .hero--zoom .hero__slide {
        transition: opacity 1ms;
        transform: none;
        clip-path: none;
    }

    .hero--slide .hero__slide {
        opacity: 0;
        visibility: hidden;
        transform: none;
    }

        .hero--slide .hero__slide.is-on {
            opacity: 1;
            visibility: visible;
            transform: none;
        }

    /* Autoplay is disabled in this branch, so navigation has to be visible at
     every width. Otherwise a phone shows one static image plus four 8px dots
     and the remaining slides are never discovered. */
    .hero:not(.hero--single) .hero__arrow {
        display: grid;
    }
}

/* ---- Single slide ---------------------------------------------------- */

.hero--single .hero__dots,
.hero--single .hero__arrow { display: none; }
