/* ─────────────────────────────────────────────────────────────────────────
   Bishop Systems seasonal kit: HALLOWEEN ORNAMENTS
   Bats and jack-o-lanterns, in depth, with motion.
   Pairs with halloween-ornaments.js. Link AFTER halloween.css.

   Optional. halloween.css alone is the restrained atmosphere-only install;
   this adds the scene on top. A client can take one without the other.

   THE RULE EVERYTHING HERE OBEYS
   Every ornament sits BEHIND the content, at low opacity, in the margins of
   the layout, and never crosses text. A visitor should feel that the site is
   dressed for October before they notice there is a bat on it. That is the
   line between seasonal and costume, and these are sites where people book a
   roof repair and file an insurance claim.

   Turn the scene off on one page without removing the files:
     <html data-season-ornaments-off>
   ───────────────────────────────────────────────────────────────────────── */

:root[data-season="halloween"] {
  --hwo-bat: #1A1526;          /* bat silhouette. Near-black, faintly violet. */
  --hwo-bat-rim: #FFB347;      /* ember rim, so the silhouette reads on dark too */
  --hwo-pumpkin: #E8802A;      /* pumpkin body */
  --hwo-pumpkin-deep: #B4530F; /* ribs and shading */
  --hwo-pumpkin-face: #FFD08A; /* lit carving */
  --hw-stem: #3F6B3A;         /* stem */
  --hwo-scene-opacity: 1;      /* global dimmer for the whole scene */
}

/* ── The stage ─────────────────────────────────────────────────────────────
   `fixed` + `pointer-events: none` + z-index 0 is the contract that makes this
   safe to drop onto a site nobody has read: it cannot shift a single element,
   cannot trap a click, cannot break a sticky header, and cannot cover a form.

   `perspective` here rather than on each ornament so every bat shares one
   vanishing point. Per-element perspective gives each its own, and the flock
   stops looking like it occupies the same sky. */
.hwo-scene {
  position: fixed;
  inset: 0;
  /* ABOVE the content, not behind it.
     At z-index 0 this sat under the first opaque section on every real client
     site and the bats were never seen once: verified on a dealership hero that
     is a full-bleed photograph. The atmosphere fog in halloween.css must stay
     BELOW content, because it is a full-viewport wash and raising it dims the
     nav and the cards. This layer is the opposite: sparse transparent SVG over
     a transparent stage, so above the content it obscures nothing and is the
     only way the flock is ever visible. `pointer-events: none` still means it
     cannot take a click from anything underneath it. */
  z-index: 40;
  pointer-events: none;
  overflow: hidden;
  perspective: 900px;
  opacity: var(--hwo-scene-opacity);
  contain: strict;
}

/* ── Bats ──────────────────────────────────────────────────────────────────
   Two nested animations, deliberately separated:
     the wrapper flies      (long, linear, crosses the screen)
     the inner bobs         (short, eased, rises and falls)
   One element doing both would need a single keyframe track combining a 30s
   traverse with a 1.5s bob, which means either a very long keyframe list or a
   bob that stutters. Nesting lets each run at its own tempo. */
.hwo-bat {
  position: absolute;
  top: var(--top, 20%);
  left: 0;
  width: calc(74px * var(--depth, 1));
  /* Depth does three things at once, which is what sells distance: further
     away is smaller, dimmer and softer. Changing only size reads as "small
     bat", not "far bat". */
  /* Tuned up from 0.10-0.30 after the first smoke test: as a dark silhouette
     on a light client site the flock was technically present and effectively
     invisible, which is the worst of both, all of the cost and none of the
     season. Still a silhouette, never a solid. */
  opacity: calc(0.34 + 0.42 * var(--depth, 1));
  filter: blur(calc((1 - var(--depth, 1)) * 2.4px));
  animation: hwo-fly var(--dur, 28s) linear infinite;
  animation-delay: var(--delay, 0s);
  animation-direction: var(--dir, normal);
  will-change: transform;
}

.hwo-bat-svg {
  display: block;
  width: 100%;
  height: auto;
  fill: var(--hwo-bat);
  /* A warm rim, and it is doing real work rather than decoration.
     A near-black silhouette is legible on a light page and completely gone on
     a dark one, and these four sites are a mix, one of them a full-bleed dusk
     photograph where the flock was invisible. The ember glow reads against a
     dark hero, the dark body reads against a light one, so one treatment
     covers both without having to detect which kind of site it landed on. */
  filter: drop-shadow(0 0 3px color-mix(in oklch, var(--hwo-bat-rim) 85%, transparent))
          drop-shadow(0 0 9px color-mix(in oklch, var(--hwo-bat-rim) 45%, transparent));
  animation: hwo-bob 2.6s ease-in-out infinite;
  animation-delay: var(--delay, 0s);
}

/* Wings beat around the shoulder, not the centre of the bounding box, or the
   whole bat appears to shear rather than flap. */
.hwo-bat-wing {
  transform-origin: 60px 30px;
  animation: hwo-flap 0.42s ease-in-out infinite;
}

.hwo-bat-wing-r {
  /* Mirrored timing so the two wings beat together rather than paddling. */
  animation-name: hwo-flap-r;
}

/* translate3d, not `left`. A transform stays on the compositor; animating
   `left` relayouts the page on every frame of a 30-second loop, on every
   client site, forever. */
@keyframes hwo-fly {
  0%   { transform: translate3d(-14vw, 0, 0) rotateY(0deg); }
  50%  { transform: translate3d(52vw, 0, -120px) rotateY(9deg); }
  100% { transform: translate3d(118vw, 0, 0) rotateY(0deg); }
}

@keyframes hwo-bob {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(-3deg); }
  50%      { transform: translate3d(0, -14px, 0) rotate(3deg); }
}

@keyframes hwo-flap {
  0%, 100% { transform: rotateY(0deg) rotateZ(0deg); }
  50%      { transform: rotateY(58deg) rotateZ(-13deg); }
}

@keyframes hwo-flap-r {
  0%, 100% { transform: rotateY(0deg) rotateZ(0deg); }
  50%      { transform: rotateY(-58deg) rotateZ(13deg); }
}

/* ── Pumpkins ──────────────────────────────────────────────────────────────
   Bottom corners only, where no layout puts content. A row along the footer
   reads as a border; two in the corners reads as a porch. */
.hwo-pumpkin {
  position: absolute;
  /* Sits ON the bottom edge, not through it. Negative bottom cropped the
     carved face, which is the only part of a jack-o-lantern worth drawing. */
  bottom: clamp(10px, 2vh, 26px);
  width: var(--size, 72px);
  opacity: calc(0.62 + 0.3 * var(--depth, 1));
  filter: blur(calc((1 - var(--depth, 1)) * 1.1px));
}

/* The right-hand pumpkin is pushed well inboard because the bottom-right
   corner is where every chat widget on earth docks, and on the dealership site
   it landed directly behind the bubble. Bottom-left is the only reliably empty
   corner, so the larger pumpkin lives there. */
.hwo-pumpkin-left  { left: clamp(6px, 2.5vw, 44px); }
.hwo-pumpkin-right { right: clamp(84px, 11vw, 190px); }

.hwo-pumpkin-svg {
  display: block;
  width: 100%;
  height: auto;
  position: relative;
  z-index: 1;
}

.hwo-pk-body { fill: var(--hwo-pumpkin); }
.hwo-pk-rib  { fill: none; stroke: var(--hwo-pumpkin-deep); stroke-width: 1.6; opacity: 0.55; }
.hwo-pk-stem { fill: var(--hw-stem); }

/* The carving is the only lit part, so it gets the glow rather than the whole
   pumpkin. A pumpkin that glows all over is a lamp; one that glows through its
   face is carved. */
.hwo-pk-face {
  fill: var(--hwo-pumpkin-face);
  filter: drop-shadow(0 0 5px color-mix(in oklch, var(--hwo-pumpkin-face) 70%, transparent));
  animation: hwo-candle 3.4s ease-in-out infinite;
  animation-delay: var(--delay, 0s);
}

/* The light it throws into the room. Separate element so it can pulse without
   the SVG repainting. */
.hwo-pk-glow {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 240%;
  aspect-ratio: 1;
  transform: translate(-50%, 32%);
  border-radius: 50%;
  background: radial-gradient(
    circle,
    color-mix(in oklch, var(--hwo-pumpkin-face) 24%, transparent) 0%,
    transparent 64%
  );
  animation: hwo-candle 3.4s ease-in-out infinite;
  animation-delay: var(--delay, 0s);
}

/* Irregular on purpose. A clean sine reads as a pulsing LED; a candle gutters. */
@keyframes hwo-candle {
  0%, 100% { opacity: 1; }
  18%      { opacity: 0.82; }
  31%      { opacity: 1; }
  47%      { opacity: 0.7; }
  58%      { opacity: 0.95; }
  74%      { opacity: 0.78; }
}

/* ── Reduced motion ────────────────────────────────────────────────────────
   The scene stays, the motion goes. Stripping the ornaments entirely would
   punish someone for a comfort setting by giving them a plainer site; freezing
   them keeps the season and removes the thing that actually causes trouble.
   The bats are parked at readable positions rather than at 0% of their flight
   path, which is off-screen. */
@media (prefers-reduced-motion: reduce) {
  .hwo-bat,
  .hwo-bat-svg,
  .hwo-bat-wing,
  .hwo-pk-face,
  .hwo-pk-glow {
    animation: none !important;
  }

  .hwo-bat { transform: translate3d(22vw, 0, 0); }
  .hwo-bat:nth-of-type(even) { transform: translate3d(68vw, 0, 0); }
}

.hwo-scene[data-reduced="true"] .hwo-bat,
.hwo-scene[data-reduced="true"] .hwo-bat-svg,
.hwo-scene[data-reduced="true"] .hwo-bat-wing,
.hwo-scene[data-reduced="true"] .hwo-pk-face,
.hwo-scene[data-reduced="true"] .hwo-pk-glow {
  animation: none !important;
}

/* ── Restraint on small screens ────────────────────────────────────────────
   The JS already skips bats under 640px. This shrinks the pumpkins so they sit
   in the corner of a phone rather than squatting across the footer. */
@media (max-width: 640px) {
  .hwo-pumpkin { --size: 46px; opacity: 0.32; }
  .hwo-pumpkin-right { display: none; }
}

/* A printed estimate does not arrive with bats on it. */
@media print {
  .hwo-scene { display: none !important; }
}
