/* ==========================================================================
   Blue Lily Petal Motion
   "Intelligence in Motion. Excellence in Bloom."

   The drift is CSS keyframes, so it runs before — and without — JavaScript.
   JavaScript contributes only the pointer parallax, by writing a transform on
   each petal of a visible field (see petals.js for why not a custom property).
   The --px/--py fallback below stays at zero unless something sets it.

   Two nested elements per petal, because they carry two independent
   transforms: the outer one takes the pointer offset, the inner one runs the
   drift keyframes. Combining them on one element would mean recomposing the
   whole transform in JS every frame.
   ========================================================================== */

:root {
  /* Petal palette (brief §4) */
  --petal-soft:     #93C5FD;
  --petal-electric: #60A5FA;
  --petal-violet:   #818CF8;
  --petal-pale:     #DBEAFE;

  /* Glow — restrained. Two small shadows read as light; one large blurred
     one reads as fog and costs far more to paint. */
  --petal-glow:        0 0 10px rgba(96, 165, 250, .34);
  --petal-glow-strong: 0 0 16px rgba(129, 140, 248, .46);

  /* Motion */
  --petal-size-hero:    30px;
  --petal-size-section: 21px;
  --petal-size-small:   15px;
  --petal-parallax:     1;      /* scaled down on tablet, zeroed on touch */
  --petal-ease:         cubic-bezier(.37, .12, .36, .92);
}

.petal-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* A field reaches past its heading on purpose, and a heading near the edge of
   the viewport would carry its petals out past the page and widen it. Each
   section clips horizontally. `clip`, not `hidden`: hidden would make every
   section a scroll container and break the sticky rails inside them, and it
   would force overflow-y to auto as well. Vertical spill stays visible. */
main section { overflow-x: clip; }

/* --------------------------------------------------------------- field --- */
.petal-heading { position: relative; }
/* The heading itself must sit above its petals. Anything that is not the
   field gets lifted, so this works whatever markup is wrapped. */
.petal-heading > *:not(.petal-field) { position: relative; z-index: 1; }

.petal-field {
  position: absolute;
  /* Reaches past the heading box so petals enter and leave from outside it
     rather than popping into existence at the edge. */
  inset: -42% -14%;
  z-index: 0;
  pointer-events: none;
  overflow: visible;
  /* Held at 0 until the section is reached; the reveal observer releases it.
     Without JS the fallback rule below turns it on. */
  opacity: 0;
  transition: opacity 1.1s var(--petal-ease);
}
.petal-field.is-blooming { opacity: 1; }

/* If JavaScript never runs, the petals are simply present. */
html:not(.js) .petal-field { opacity: 1; }

/* --------------------------------------------------------------- petal --- */
.petal {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: calc(var(--petal-size, var(--petal-size-section)) * var(--s));
  aspect-ratio: 20 / 45;
  /* Outer transform: pointer parallax only, deeper petals move further. */
  transform: translate3d(
    calc(var(--px, 0px) * var(--depth) * var(--petal-parallax)),
    calc(var(--py, 0px) * var(--depth) * var(--petal-parallax)), 0);
  transition: transform .9s var(--petal-ease);
}

.petal__drift {
  display: block;
  width: 100%;
  height: 100%;
  /* Opacity is NOT animated: the sway loops seamlessly, so there is no seam
     for a fade to hide, and the compositor is spared an opacity write every
     frame. The petal simply holds its peak -- the REVEAL belongs to the field
     (.petal-field.is-blooming), and must stay there. Gating this on
     .is-blooming as well made the two multiply, so a field that never bloomed
     left every petal invisible instead of merely unrevealed. */
  opacity: var(--peak);
  transform-origin: 50% 82%;
  animation: petal-drift var(--dur) ease-in-out infinite;
  animation-delay: var(--phase);
  position: relative;
}
/* The second motion. Its period is deliberately NOT a multiple of the first,
   so the two never come back into step and the drift does not read as a
   loop -- see the note above @keyframes petal-drift. */
.petal__drift > svg {
  animation: petal-lift var(--dur2) ease-in-out infinite;
  animation-delay: var(--phase2);
  transform-origin: 50% 70%;
  will-change: transform;
}
.petal svg { display: block; position: relative; width: 100%; height: 100%; }
/* The glow is painted, not filtered. A drop-shadow filter on each moving petal
   was the single largest cost on the page -- a filter pass per petal per
   frame, measured at 32fps against 48 without it. A soft radial gradient
   behind the petal is rasterised once, with the petal, and then only moved. */
.petal__drift::before {
  content: '';
  position: absolute;
  inset: -40% -100%;
  background: radial-gradient(closest-side, var(--petal-halo, rgba(96, 165, 250, .3)), transparent);
  opacity: .85;
  transition: opacity .6s ease;
  pointer-events: none;
}

/* Curved travel: the x and y components peak at different points in the
   cycle, which is what bends the path. Opacity fades in and out at the ends
   so a petal is never seen appearing or vanishing. */
/* TWO MOTIONS, PERIODS THAT DO NOT DIVIDE.
   A petal that retraces one path on one period reads as a loop however the
   phase is randomised. So the horizontal sway below runs on --dur and the
   rise below it on --dur2, which is 1.41-1.87x --dur and never a round
   multiple. The compound path repeats only at the lowest common multiple of
   the two -- tens of minutes -- so it never visibly repeats.

   Both are ASYMMETRIC on purpose: the petal goes out further than it comes
   back, and its turn peaks away from the middle, so neither motion reads as
   a pendulum even on its own. */
@keyframes petal-drift {
  0%, 100% {
    transform: translate3d(0, 0, 0) rotate(var(--tilt));
  }
  31% {
    transform: translate3d(calc(var(--dx) * .84), 0, 0)
               rotate(calc(var(--tilt) + var(--spin) * .14));
  }
  54% {
    transform: translate3d(calc(var(--dx) * .29), 0, 0)
               rotate(calc(var(--tilt) - var(--spin) * .05));
  }
  78% {
    transform: translate3d(calc(var(--dx) * .96), 0, 0)
               rotate(calc(var(--tilt) + var(--spin) * .19));
  }
}
/* NO SCALE. A petal that pulses as it drifts reads as a cheap loop however
   slowly it moves; a brand mark should drift, not breathe. The rotation is
   also cut to about a third -- at the old amount the petals visibly wobbled,
   which is the difference between weather and a wind-up toy. */
@keyframes petal-lift {
  0%, 100% { transform: translate3d(0, 0, 0); }
  26%  { transform: translate3d(0, calc(var(--dy) * .92), 0); }
  49%  { transform: translate3d(0, calc(var(--dy) * .18), 0); }
  71%  { transform: translate3d(0, calc(var(--dy) * .68), 0); }
  88%  { transform: translate3d(0, calc(var(--dy) * .22), 0); }
}

/* ------------------------------------------------------------ densities --- */
.petal-field--hero    .petal { --petal-size: var(--petal-size-hero); }
.petal-field--section .petal { --petal-size: var(--petal-size-section); }
.petal-field--small   .petal { --petal-size: var(--petal-size-small); }

/* A heading being hovered brightens its cluster slightly. Restrained: the
   glow changes, the motion does not. */
@media (hover: hover) and (pointer: fine) {
  .petal-heading:hover .petal__drift::before { opacity: 1; }
}

/* On deep navy the petals are the brightest thing behind white text, so the
   ceiling set in petals.py is reinforced here and the glow is cooled. */
.band-dark .petal__drift,
.feature-band .petal__drift,
.cta-band .petal__drift,
.v2--navy .petal__drift,
.v2--indigo .petal__drift {
  --petal-halo: rgba(147, 197, 253, .26);
}

/* -------------------------------------------------------------- responsive */
@media (max-width: 1024px) {
  :root { --petal-parallax: .55; }
  /* Thin the clusters rather than shrinking them: fewer, same size, so the
     motif stays legible instead of turning into specks. */
  .petal-field--hero .petal:nth-child(n + 9),
  .petal-field--section .petal:nth-child(n + 5),
  .petal-field--small .petal:nth-child(n + 3) { display: none; }
}

@media (max-width: 720px) {
  :root {
    --petal-size-hero: 22px;
    --petal-size-section: 16px;
    --petal-size-small: 12px;
  }
  .petal-field { inset: -30% -8%; }
  .petal-field--hero .petal:nth-child(n + 6),
  .petal-field--section .petal:nth-child(n + 4) { display: none; }
}

/* Pointer parallax is meaningless without a pointer, and the transition would
   only cost battery. */
@media (hover: none), (pointer: coarse) {
  :root { --petal-parallax: 0; }
  .petal { transition: none; }
}

/* ------------------------------------------------------- reduced motion --- */
/* The composition survives; only the movement stops. Petals settle at a
   mid-drift pose so the cluster still reads as arranged rather than dumped
   in a row. */
@media (prefers-reduced-motion: reduce) {
  .petal__drift > svg { animation: none; transform: none; }
  .petal__drift {
    animation: none;
    opacity: var(--peak);
    transform: translate3d(calc(var(--dx) * .45), calc(var(--dy) * .5), 0)
               rotate(calc(var(--tilt) + var(--spin) * .45));
  }
  .petal { transition: none; transform: none; }
  .petal-field { transition: none; opacity: 1; }
}

/* Paused while the tab is hidden, and while a field is off screen. Both are
   set from JS; the selector costs nothing when it is not applied. */
.petals-paused .petal__drift,
.petals-paused .petal__drift > svg { animation-play-state: paused; }

/* Once petals.js is driving the parallax it eases every frame itself; a CSS
   transition on top would re-ease it and restart on every write. */
.petal-field.is-driven .petal { transition: none; }
