/* ======================================================================
   Porta Productions — the moving background.

   Three approaches, one file. Switch with one attribute on <html>:

       <html data-bg="drift">      ← shipped default
       <html data-bg="parallax">
       <html data-bg="grain">

   Rules every approach obeys, because these are what decide the design:

     · It sits behind text, so it may never cost contrast. The widest tonal
       swing any of these puts on the ground is about three points of
       lightness on brand navy — #121A25 at its darkest, roughly #1E2836 at
       its brightest. Body text at #C9CBCE stays above 9:1 either way, and
       the WCAG AA floor is 4.5:1.
     · No glass, no backdrop-filter. Measured at up to +193% render cost and
       it ignores prefers-reduced-motion.
     · It must cost close to nothing. Everything here animates `transform`
       and `opacity` only, on a `position:fixed` layer that never scrolls,
       so the work lands on the compositor and not on the main thread.
       Nothing here animates a gradient, a colour, a size or a position.
     · It respects prefers-reduced-motion and falls back to a static state
       that keeps the light and drops the movement.
     · It does not enter the reviews section. That section fills its own
       ground and owns the one bloom on the page. One section has a bloom
       and no drift; every other section has drift and no bloom.
     · Navy only. No accent.

   Original work: these are two soft navy light fields and a grain tile,
   built here from gradients. Nothing is copied from or modelled on another
   company's treatment.
   ====================================================================== */

/* The ground moves from <body> to <html> so the fixed layer can sit above
   it and below every piece of content, with no stacking surprises. That
   move is done in restyle.js, using the page's own ground colour, because
   a light page such as /prepare/ must keep its own and gets no field at
   all. Doing it here would hard-code navy onto every page.

   The field is only injected on dark-ground pages. */

.pp-bg{
  position:fixed;inset:0;z-index:-1;
  pointer-events:none;overflow:hidden;
  contain:layout paint;
}
.pp-bg span{
  position:absolute;display:block;
  will-change:transform;
  transform:translate3d(0,0,0);
  backface-visibility:hidden;
}

/* The dark sections become translucent so the field runs continuously
   underneath them instead of stopping at each one. Their tonal step is
   preserved; it is now a dimming of the ground rather than a second
   opaque ground. */
.darker{
  background:linear-gradient(180deg,rgba(14,20,28,.74),rgba(9,13,19,.88)) !important;
}
/* The light sections stay opaque. A navy light field has no business in a
   white room, and stopping there costs nothing. */

/* The reviews section is the exception in both directions: it is opaque, so
   the field stops at it, and it is the only place a bloom is allowed. */
[data-bloom],
.darker[data-bloom]{background:var(--ink1,#121A25) !important;}
.paper[data-bloom]{background:#F5F6F7 !important;}

/* ======================================================================
   A · DRIFT  — the shipped default
   Two light fields on slow, opposed paths. A wide band low in the frame,
   like a city glow sitting below the bottom edge, and a soft high wash
   above it. 120s and 94s, so nothing ever repeats where you are looking.
   Cost: two composited layers, transform-only. No main-thread work at all
   once the first frame is up.
   ====================================================================== */
[data-bg="drift"] .pp-bg .f1{
  left:-34%;right:-34%;bottom:-26%;height:96%;
  background:linear-gradient(0deg,
    rgba(58,80,110,.80) 0%,
    rgba(40,56,78,.42) 32%,
    rgba(18,26,37,0) 72%);
  animation:ppDriftA 46s var(--std,cubic-bezier(.4,0,.2,1)) infinite alternate;
}
[data-bg="drift"] .pp-bg .f2{
  left:-26%;right:-26%;top:-30%;height:110%;
  background:radial-gradient(54% 42% at 50% 48%,
    rgba(72,98,134,.46) 0%,
    rgba(38,53,74,.20) 46%,
    rgba(18,26,37,0) 74%);
  animation:ppDriftB 34s var(--std,cubic-bezier(.4,0,.2,1)) infinite alternate;
}
/* The two paths are deliberately different lengths and different periods,
   so they never line up twice in the same place. 46s and 34s share no
   common factor short enough to notice. */
@keyframes ppDriftA{
  from{transform:translate3d(-9%, 3%,0) scale(1.00);}
  to  {transform:translate3d( 9%,-4%,0) scale(1.14);}
}
@keyframes ppDriftB{
  from{transform:translate3d( 7%,-5%,0) scale(1.12);}
  to  {transform:translate3d(-7%, 6%,0) scale(1.00);}
}

/* ======================================================================
   B · GRAIN
   A film grain tile over the navy, swimming slowly. Texture rather than
   light: it stops flat navy reading as a default, the way grain does on
   his footage.
   Cost: one 180px tile, rasterised once and reused; transform-only
   animation. Cheap, but see the honest note in the session report — at the
   opacity where you can feel it, it starts to read as noise on a phone.
   ====================================================================== */
[data-bg="grain"] .pp-bg .f1{
  left:-10%;right:-10%;top:-10%;bottom:-10%;
  opacity:.055;
  background-repeat:repeat;
  background-size:180px 180px;
  background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  animation:ppGrain 24s steps(1,end) infinite;
}
/* A second, much larger and softer field underneath, so the grain has
   something to sit on and the whole frame is not uniformly flat. */
[data-bg="grain"] .pp-bg .f2{
  left:-24%;right:-24%;bottom:-24%;height:92%;
  background:linear-gradient(0deg,
    rgba(54,74,102,.62) 0%,rgba(18,26,37,0) 70%);
  animation:ppDriftA 70s var(--std,cubic-bezier(.4,0,.2,1)) infinite alternate;
}
/* Whole-pixel steps, so the tile never lands between pixels and shimmers. */
@keyframes ppGrain{
  0%  {transform:translate3d(0,0,0);}
  20% {transform:translate3d(-13px,7px,0);}
  40% {transform:translate3d(9px,-11px,0);}
  60% {transform:translate3d(-6px,-5px,0);}
  80% {transform:translate3d(12px,9px,0);}
  100%{transform:translate3d(0,0,0);}
}

/* ======================================================================
   C · PARALLAX
   The same two light fields as A, but the page moves past them instead of
   them moving on their own. Scroll drives the transform directly; when the
   page is still, nothing runs at all.
   Cost: one transform write per animation frame while scrolling, scheduled
   on rAF, and zero when the page is still. See restyle.js.
   ====================================================================== */
[data-bg="parallax"] .pp-bg .f1{
  left:-34%;right:-34%;bottom:-34%;height:104%;
  background:linear-gradient(0deg,
    rgba(58,80,110,.80) 0%,
    rgba(40,56,78,.42) 32%,
    rgba(18,26,37,0) 72%);
}
[data-bg="parallax"] .pp-bg .f2{
  left:-26%;right:-26%;top:-40%;height:124%;
  background:radial-gradient(54% 42% at 50% 48%,
    rgba(72,98,134,.46) 0%,
    rgba(38,53,74,.20) 46%,
    rgba(18,26,37,0) 74%);
}

/* ======================================================================
   Reduced motion — the light stays, the movement does not.
   Both fields hold a composed static pose. Parallax never attaches its
   scroll listener at all (restyle.js checks first).
   ====================================================================== */
@media(prefers-reduced-motion:reduce){
  .pp-bg span{animation:none !important;}
  [data-bg="drift"] .pp-bg .f1,
  [data-bg="grain"] .pp-bg .f2{transform:translate3d(0,0,0) scale(1.04);}
  [data-bg="drift"] .pp-bg .f2{transform:translate3d(0,0,0) scale(1.03);}
}

/* Print: no field at all. */
@media print{.pp-bg{display:none;}}
