/* ==========================================================================
   Ferrands Home Hero — "tastes SO GOOD!" banner-plate v2 + five-tub cluster
   Namespace: .ffh- (Ferrands Food Hero)

   All percentages are measured off the v2 plate (1536 x 1024):
     panel inner  left 50.59%  top 14.26%  width 44.40%  height 74.22%
     panel floor  82.5% of the panel's inner height
     CTA button   left  8.40%  top 73.24%  width 24.87%  height  9.18%

   Per-tub layout lives in one block per flavour below. --base is the tub's
   BASE measured down from the panel's top edge: the further forward a tub
   stands on the floor, the larger --base, and the larger --w.
   ========================================================================== */

.ffh-hero {
  --ffh-ink: #141414;

  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  background: linear-gradient(135deg, #C2014E 0%, #C2014E 45%, #0293E5 55%, #0293E5 100%);
}

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

/* ---- the banner-plate image, full-bleed, aspect-ratio locked ---- */

.ffh-banner {
  position: relative;
  width: 100%;
  /* No width cap: the plate fills the viewport at any width. Previously
     capped at 1920px, which left 240px of gradient band either side of the
     banner on a 2400px screen.

     The aspect-ratio lock below is LOAD-BEARING and must not be removed. The
     tub cluster and the invisible CTA are positioned in percentages of this
     box, measured off the plate's own pixels. If the box aspect ever diverges
     from the image aspect, object-fit: cover crops the art and every overlay
     drifts off the thing it is aligned to.

     Consequence of no cap: because the aspect is locked, extra width becomes
     extra height (width / 1.5). On very wide displays the hero grows tall --
     2560px wide renders 1707px tall. If that becomes a problem, reinstate a
     generous cap here rather than touching the aspect ratio. */
  max-width: none;
  margin: 0 auto;
  aspect-ratio: 1536 / 1024;
  overflow: hidden;
}

.ffh-banner__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---- real, invisible link over the baked-in "Find Ferrands Near You" ---- */

.ffh-cta-hit {
  position: absolute;
  left: 8.4%;
  top: 73.24%;
  width: 24.87%;
  height: 9.18%;
  border-radius: 999px;
  display: block;
  z-index: 8;
}

.ffh-cta-hit:focus-visible {
  outline: 3px solid #FFFFFF;
  outline-offset: 3px;
  box-shadow: 0 0 0 6px rgba(2, 147, 229, 0.45);
}

@media (hover: hover) and (pointer: fine) {
  .ffh-cta-hit:hover { background: #0079CC; }
}

.ffh-mpatch {
  display: block;
  position: absolute;
  left: 0;
  z-index: 3;
  pointer-events: none;
}

/* Patch geometry, global.
   These were originally nested in the mobile media query. When the patches
   were ungated the base rule moved out but these did not, so above 767px
   the elements had display:block with no size: they collapsed to 0x0 and
   the baked pill stayed visible underneath the new button. */



/* field above the floor line. The polygon's top edge follows the button top
   (73.24%) except where the swoosh dips below it, so the patch can never
   paint over magenta. */
.ffh-mpatch--field {
  top: 71%;
  width: 37%;
  height: 12.203%;
  background: linear-gradient(180deg, #019AEE 0%, #0198ED 70%, #0296EB 100%);
  clip-path: polygon(0.0% 18.4%, 16.2% 21.8%, 20.3% 25.0%, 28.4% 18.6%, 97.3% 18.4%, 100.0% 18.4%, 100% 100%, 0% 100%);
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 88%, transparent 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 88%, transparent 100%);
}


/* the glossy floor, replacing the mirrored pill. Stops measured down the
   plate's own floor ramp, so the top seam lands inside the 83.203% step. */
.ffh-mpatch--floor {
  top: 83.203%;
  width: 38%;
  height: 16.797%;
  background: linear-gradient(180deg,
    #008BE2 0%, #018FE5 16%, #019AF0 34%, #03A1F5 51%,
    #06A4F7 69%, #05A4F7 86%, #04A2F7 100%);
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 84%, transparent 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 84%, transparent 100%);
}

/* ---- the real CTA, desktop ----
   Occupies exactly the footprint the baked pill had (measured off plate
   1001: left 8.40%, top 73.24%, 24.87% x 9.18%), so the composition is
   unchanged while the label becomes real, selectable, resizable text.
   #006FBE is sampled from the baked pill's own 3D lip and gives white
   5.23:1, passing AA where the original 3.15:1 did not. The fill alone is
   only 1.71:1 against the patched cyan field, so the 2px #141414 ring is
   load-bearing for WCAG 1.4.11 at 6.02:1, not decoration. */
.ffh-cta-hit {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  background: #006FBE;
  color: #FFFFFF;
  text-decoration: none;
  font-weight: 800;
  /* Sized to sit near the baked original's presence, not merely to pass.
     The pixels it replaces rendered a 25.3px cap height at a 1440 viewport;
     a first pass at 1.22vw gave 12.4px, which cleared AA but read as a much
     weaker button than the artwork had. 2.0vw lands close to the original
     while the 14px floor keeps the label inside the 24.87%-wide box at the
     768px breakpoint, where that box is only ~191px across. */
  font-size: clamp(16px, 2.0vw, 32px);
  /* The pill may grow rightward past the baked footprint when the label
     needs it. The artwork's own label used a narrower face than the
     theme's Nunito, so holding the exact 24.87% width forced the type
     down to ~14px (a 10px cap, under the legible floor) at the 768px end.
     Growing is safe here: the field beside the pill is patched flat cyan,
     and the cap of 30% keeps the right edge at 38.4%, clear of the nearest
     baked sprinkle at 38.87%. Measured fits at 768/820/1100/1440/1920. */
  width: auto;
  min-width: 24.87%;
  max-width: 30%;
  line-height: 1;
  letter-spacing: 0.01em;
  padding: 0 clamp(10px, 1.0vw, 24px);
  box-shadow: inset 0 0 0 2px #141414, 0 6px 0 0 #004C8B, 0 14px 20px rgba(0, 38, 66, 0.28);
  transition: transform 0.14s, box-shadow 0.14s;
}
.ffh-cta-hit:active {
  transform: translateY(3px);
  box-shadow: inset 0 0 0 2px #141414, 0 3px 0 0 #004C8B, 0 8px 14px rgba(0, 38, 66, 0.26);
}
.ffh-cta-label { display: inline-flex; align-items: center; white-space: nowrap; }

/* ---- the panel zone: the inner face of the rounded plate in the image ---- */

.ffh-panel-zone {
  position: absolute;
  left: 50.59%;
  top: 14.26%;
  width: 44.4%;
  height: 74.22%;
  pointer-events: none;
  /* hard clip: reflections are taller than the floor area below each tub and
     must never paint over the plate's bottom border or the sky behind it */
  overflow: hidden;
}

/* ---- per-flavour placement on the panel floor ---- */
/*  GENTLE ARC / FAN.
    The panel floor runs from 82.5% (where it meets the back wall) to 100%
    (nearest the camera), so "further back" means a SMALLER --base. The arc is
    built by walking --base outward-to-inward: the outer tubs stand near the
    back of the floor and sit higher on the panel, the centre tub stands at the
    front and sits lowest. Sizes grow inward for the same reason, and --rot
    fans symmetrically from -10deg on the far left to +10deg on the far right.
    Resulting top edges arc across roughly 8% of the panel height.            */

/*  far left — furthest back, smallest, leaning out                          */
.ffh-tub--peanut,     .ffh-reflect--peanut,     .ffh-shadow--peanut
  { --l:  2%; --w: 32%; --base: 83%; --rot: -10deg; --dur: 2.5s; --delay: .05s; --z: 2; }
/*  left                                                                      */
.ffh-tub--chocolate,  .ffh-reflect--chocolate,  .ffh-shadow--chocolate
  { --l: 17%; --w: 35%; --base: 90%; --rot:  -5deg; --dur: 2.1s; --delay: .40s; --z: 3; }
/*  centre — furthest forward, largest, upright                              */
.ffh-tub--vanilla,    .ffh-reflect--vanilla,    .ffh-shadow--vanilla
  { --l: 31%; --w: 38%; --base: 97%; --rot:   0deg; --dur: 1.9s; --delay:   0s; --z: 4; }
/*  right                                                                     */
.ffh-tub--strawberry, .ffh-reflect--strawberry, .ffh-shadow--strawberry
  { --l: 48%; --w: 35%; --base: 90%; --rot:   5deg; --dur: 2.3s; --delay: .20s; --z: 3; }
/*  far right — furthest back, smallest, leaning out                         */
.ffh-tub--banana,     .ffh-reflect--banana,     .ffh-shadow--banana
  { --l: 66%; --w: 32%; --base: 83%; --rot:  10deg; --dur: 2.7s; --delay: .30s; --z: 2; }

/* ---- the tubs ---- */

.ffh-tub {
  position: absolute;
  left: var(--l);
  width: var(--w);
  height: auto;
  bottom: calc(100% - var(--base));
  z-index: var(--z);
  will-change: transform;
  transform-origin: 50% 100%;
  filter: drop-shadow(0 14px 18px rgba(4, 14, 26, 0.2));
  animation: ffh-float var(--dur) ease-in-out var(--delay) infinite alternate;
  pointer-events: auto;
}

/* ---- contact shadows on the floor ---- */

.ffh-tub-shadow {
  position: absolute;
  left: calc(var(--l) + 4%);
  width: calc(var(--w) - 8%);
  height: 4%;
  bottom: calc(100% - var(--base) - 1.6%);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(4, 14, 26, 0.48), rgba(4, 14, 26, 0) 72%);
  filter: blur(3px);
  z-index: 1;
  animation: ffh-shadow var(--dur) ease-in-out var(--delay) infinite alternate;
}

/* ---- mirrored reflections on the panel's glossy floor ---- */

.ffh-tub-reflection {
  position: absolute;
  left: var(--l);
  width: var(--w);
  height: auto;
  top: var(--base);
  z-index: 0;
  opacity: 0.18;
  will-change: transform;
  transform-origin: 50% 0%;
  animation: ffh-float-reflect var(--dur) ease-in-out var(--delay) infinite alternate;
  -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.9), transparent 30%);
  mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.9), transparent 30%);
  pointer-events: none;
}

/* ---- motion: faster and higher than the first pass ---- */

@keyframes ffh-float {
  from { transform: translateY(0)     rotate(var(--rot)); }
  to   { transform: translateY(-24px) rotate(calc(var(--rot) + 2deg)); }
}

/* scaleY(-1) first: inside the flipped frame, translateY(-24px) reads as
   "down", which is what a true mirror of a tub lifting off does. */
@keyframes ffh-float-reflect {
  from { transform: scaleY(-1) translateY(0)     rotate(var(--rot)); }
  to   { transform: scaleY(-1) translateY(-24px) rotate(calc(var(--rot) + 2deg)); }
}

@keyframes ffh-shadow {
  from { transform: scaleX(1);    opacity: 1; }
  to   { transform: scaleX(0.82); opacity: 0.55; }
}

/* ---- hover (fine pointer only) ---- */

@media (hover: hover) and (pointer: fine) {
  .ffh-tub { transition: transform 0.3s cubic-bezier(0.2, 0.7, 0.3, 1), filter 0.3s; }
  .ffh-tub:hover {
    animation-play-state: paused;
    transform: translateY(-32px) rotate(0deg) scale(1.06);
    filter: drop-shadow(0 30px 34px rgba(4, 14, 26, 0.3));
    z-index: 9;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ffh-tub,
  .ffh-tub-reflection,
  .ffh-tub-shadow { animation: none !important; }
  .ffh-tub { transform: rotate(var(--rot)) !important; }
  .ffh-tub-reflection { transform: scaleY(-1) rotate(var(--rot)) !important; }
}


/* ==========================================================================
   MOBILE TREATMENT  (<= 767px)
   The plate's button is baked pixels. At 375px it renders a 6.6px cap-height
   label inside a 93.3 x 22.9px tap target -- failing legibility (~11px
   practical floor) and WCAG 2.5.5 / HIG (44 x 44). It also carries a 3.15:1
   white-on-cyan contrast, which fails WCAG 1.4.3 AA for normal text at every
   breakpoint.

   Fix: below 767px, erase the pill AND its baked floor reflection with two
   gradient patches, then stand a real <a> on a short apron that continues the
   glossy floor. No new asset, no extra HTTP request, no JS.

   Breakpoint rationale -- baked button height = 0.0612 x viewport width, so it
   crosses 44px at vw=719; cap-height = 0.0176 x vw, crossing 11px at vw=626.
   767px is Elementor's own mobile breakpoint and sits above the binding 719px
   threshold, so no new breakpoint is introduced.

   EVERY number below was measured off plate 1001 itself and verified by
   compositing offline before shipping:
     button          left 8.40%  top 73.24%  w 24.87%  h 9.18%
     swoosh underside  y 73.14% flat, dipping to 74.02% at x 6-10%
                       (it overlaps the pill's top-left cap, so a plain
                        rectangular patch is impossible -- hence the polygon)
     floor line        83.203%  (a hard 8.5-level step; the seam hides in it)
     baked reflection  x 7.81-34.11%,  y 85.06-96.09%,  peak 25 levels
     nearest sprinkle  x 38.87%  (both patches stop short of it)
   Result offline: button-zone R-std 64.17 -> 13.71 (clean field reads 27.62),
   reflection-zone 10.07 -> 2.12 (clean field 4.10), swoosh magenta delta
   -0.027% (antialiased edge only), logo/lettering/panel byte-identical.
   ========================================================================== */

/* The patches now run at EVERY width, not just mobile. Their geometry is
   percentage-based off the plate, so it holds at any size, and the baked
   pill fails WCAG 1.4.3 on desktop too: white on #0194EB measures 3.15:1,
   under the 4.5 needed for normal text. It only escaped on wide screens by
   rendering large enough to claim the large-text exemption. Erasing it
   everywhere and drawing a real button is the only way to fix the contrast
   without re-exporting the artwork. */
.ffh-apron { display: none; }

@media (max-width: 767px) {

  /* at 375px this box is only 93 x 23px, which cannot hold a 44px target,
     so below the breakpoint the apron button takes over instead */
  .ffh-cta-hit { display: none; }

  /* ---- the apron: the floor runs off the plate and the button stands on it.
     The 90deg stops are the plate's own bottom row, so the banner/apron
     junction has no seam; the 180deg layer is a soft shade so the floor
     reads as receding. ---- */
  .ffh-apron {
    display: block;
    position: relative;
    padding: 18px 16px 12px;
    text-align: center;
    background:
      linear-gradient(180deg, rgba(0, 26, 48, 0) 0%, rgba(0, 26, 48, 0.10) 100%),
      linear-gradient(90deg, #04A2F7 0%, #06A6F9 26%, #08AAFA 52%, #06A6F9 78%, #01A0F4 100%);
  }

  .ffh-cta-mobile {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    width: min(340px, 100%);
    min-height: 48px;
    padding: 13px 26px;
    border-radius: 999px;
    /* #006FBE is sampled from the baked pill's own 3D lip, and gives white
       text 5.23:1 -- passing WCAG 1.4.3 AA, which the baked 3.15:1 never did.
       The pill fill alone is only 1.87:1 against the apron floor, so the
       2px #141414 ring is load-bearing for WCAG 1.4.11, not decoration:
       it reads 6.59:1 against the floor. */
    background: #006FBE;
    color: #FFFFFF;
    font-weight: 800;
    font-size: 17px;
    line-height: 1;
    letter-spacing: 0.01em;
    text-decoration: none;
    box-shadow: inset 0 0 0 2px #141414, 0 6px 0 0 #004C8B, 0 14px 18px rgba(0, 38, 66, 0.30);
    transition: transform 0.14s, box-shadow 0.14s;
  }
  .ffh-cta-mobile:active {
    transform: translateY(3px);
    box-shadow: inset 0 0 0 2px #141414, 0 3px 0 0 #004C8B, 0 8px 12px rgba(0, 38, 66, 0.26);
  }
  .ffh-cta-mobile:focus-visible {
    outline: 3px solid #FFFFFF;
    outline-offset: 3px;
  }
  .ffh-cta-arrow { font-weight: 700; }

  /* a faint mirror of the button, so the apron still behaves like the plate's
     reflective floor. Same idiom as .ffh-tub-reflection. */
  .ffh-cta-ghost {
    display: block;
    width: min(340px, 100%);
    margin: 2px auto 0;
    height: 30px;
    line-height: 30px;
    border-radius: 999px;
    color: rgba(255, 255, 255, 0.55);
    font-weight: 800;
    font-size: 17px;
    transform: scaleY(-1);
    opacity: 0.20;
    filter: blur(0.6px);
    -webkit-mask-image: linear-gradient(to top, transparent 20%, rgba(0, 0, 0, 0.9) 100%);
    mask-image: linear-gradient(to top, transparent 20%, rgba(0, 0, 0, 0.9) 100%);
    pointer-events: none;
    user-select: none;
  }
}

@media (max-width: 767px) and (prefers-reduced-motion: reduce) {
  .ffh-cta-mobile { transition: none !important; }
}

/* ==========================================================================
   PHASE 3 — HERO, layout="cycle"  (v4.0.0)
   The cluster layout above is untouched and still reachable with
   [ff_home_hero layout="cluster"]. Everything below is additive.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Framed presentation
   The plate sits on the cream page inside a frame, rather than full-bleed.
   -------------------------------------------------------------------------- */
.ffh-hero--cycle{
  background:#FFF8EC;
  padding:40px 24px 64px;        /* 40 above, 64 below, per the approved mockup */
  position:relative;
}
.ffh-hero--cycle .ffh-frame{
  max-width:1120px; margin-inline:auto;
  border:3px solid #141414; border-radius:36px;
  box-shadow:12px 12px 0 #141414;
  overflow:hidden;               /* clips the plate's corners to the radius */
  position:relative; z-index:1;
}
/* The aspect-ratio lock on .ffh-banner is load-bearing (see above) and is
   inherited unchanged; the frame only constrains width. */

@media (max-width:767px){
  .ffh-hero--cycle{ padding:20px 16px 40px; }
  .ffh-hero--cycle .ffh-frame{ border-radius:24px; box-shadow:7px 7px 0 #141414; }
}

/* Sprinkles live in the side gutters, desktop only — below 1200px the frame
   is close enough to the viewport edge that there is no gutter to fill. */
.ffh-hero__sprinkles{ position:absolute; inset:0; pointer-events:none; z-index:0; }
.ffh-hero__sprinkles span{
  position:absolute; width:26px; height:8px; border-radius:9px;
}
@media (max-width:1199px){ .ffh-hero__sprinkles{ display:none; } }

/* --------------------------------------------------------------------------
   The cycling tub
   Geometry is derived from the plate measurements, converted from
   plate-relative to panel-relative because the tub lives inside .ffh-panel-zone
   (left 50.59%, top 14.26%, width 44.40%, height 74.22% of the plate):
     centre X  72.8% of plate  ->  (72.8 - 50.59) / 44.40 = 50.0% of panel
     base Y    81.7% of plate  ->  (81.7 - 14.26) / 74.22 = 90.9% from panel top
                                   => bottom: 9.1% of panel height
     height    58.0% of plate  ->   58.0 / 74.22            = 78.1% of panel height
   -------------------------------------------------------------------------- */
.ffh-cycle{ position:absolute; inset:0; pointer-events:none; }

.ffh-cycle__tub{
  position:absolute;
  left:50%;
  bottom:9.1%;
  height:78.1%;
  width:auto;
  transform:translateX(-50%);
  object-fit:contain;
  /* Default state is the no-JS / no-animation state: only the first tub shows. */
  opacity:0;
}
.ffh-cycle__tub:first-of-type{ opacity:1; }

/* Soft ellipse contact shadow, sitting on the plate's floor under the tub. */
.ffh-cycle__shadow{
  position:absolute; left:50%; bottom:7.4%;
  width:30%; height:3.4%;
  transform:translateX(-50%);
  border-radius:50%;
  background:radial-gradient(ellipse at center, rgba(20,20,20,.34) 0%, rgba(20,20,20,.16) 55%, rgba(20,20,20,0) 72%);
}

/* --------------------------------------------------------------------------
   The cycle itself. Enabled only once JS adds .ffh-cycle--on, so with JS off
   the markup renders a single static Vanilla tub (the rule above).
   12s loop, four tubs, each on screen ~3s (25% of the loop).
   -------------------------------------------------------------------------- */
.ffh-cycle--on .ffh-cycle__tub{
  animation:ffh-cycle 12s linear infinite;
  animation-delay:calc(var(--i) * 3s);
}
.ffh-cycle--on .ffh-cycle__tub:first-of-type{ opacity:0; }

@keyframes ffh-cycle{
  0%    { opacity:0; transform:translateX(-50%) translateY(22px) scale(.92) rotate(-4deg); }
  3%    { opacity:1; transform:translateX(-50%) translateY(0)    scale(1)   rotate(0deg); }
  22%   { opacity:1; transform:translateX(-50%) translateY(0)    scale(1)   rotate(0deg); }
  25%   { opacity:0; transform:translateX(-50%) translateY(-10px) scale(1.03) rotate(3deg); }
  100%  { opacity:0; transform:translateX(-50%) translateY(22px) scale(.92) rotate(-4deg); }
}

/* Reduced motion: hold on Vanilla, static. Same end state as no-JS. */
@media (prefers-reduced-motion:reduce){
  .ffh-cycle--on .ffh-cycle__tub{ animation:none; opacity:0; }
  .ffh-cycle--on .ffh-cycle__tub:first-of-type{ opacity:1; }
}
